#! /usr/bin/env perl
# SPDX-License-Identifier: Apache-2.0
# Copyright 2020 Western Digital Corporation or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

use strict;   # Do not turn this off or else
use Data::Dumper;
use Getopt::Long;
#use Bit::Vector;
use lib "$ENV{RV_ROOT}/tools";
use JSON;

my ($self) = $0 =~ m/.*\/(\w+)/o;
my @argv_orig = @ARGV;


# Master configuration file
#
# Configuration is perl hash
# Output are define files for various flows
#   Verilog (`defines common to RTL/TB)
#   Software (#defines)
#   Whisper (JSON/#defines)
#
#   Default values and valid ranges should be specified
#   Can be overridden via the cmd line (-set=name=value-string)
#
#  Format of the hash is
#    name => VALUE | LIST | HASH
#
#    Special name "inside" followed by list .. values must be one of provided list
#    Special name "derive" followed by equation to derive
#

# Dump verilog/assembly macros in upper case
my $defines_case = "U";

# Include these macros in verilog (pattern matched)
my @verilog_vars = qw (xlen config_key reset_vec tec_rv_icg numiregs nmi_vec target protection.* testbench.* dccm.* retstack core.* iccm.* btb.* bht.* icache.* pic.* regwidth memmap bus.*);

# Include these macros in assembly (pattern matched)
my @asm_vars = qw (xlen reset_vec nmi_vec target dccm.* iccm.* pic.* memmap bus.* testbench.* protection.* core.*);
my @asm_overridable = qw (reset_vec nmi_vec) ;

# Include these macros in PD (pattern matched)
my @pd_vars = qw (physical retstack target btb.* bht.* dccm.* iccm.* icache.* pic.* reset_vec nmi_vec build_ahb_lite datawidth bus.*);

# Dump non-derived/settable vars/values for these vars in stdout :
my @dvars = qw(retstack btb bht core dccm iccm icache pic bus protection memmap);

# Prefix all macros with
my $prefix = "RV_";
# No prefix if keyword has
my $no_prefix = 'RV|TOP|^tec_|regwidth|clock_period|assert_on|^datawidth|^physical|verilator|SDVT_AHB';

my $vlog_use__wh = 1;

my %regions_used = ();

# Cmd Line options#{{{
our %sets;
our %unsets;
my $help;
my @sets = ();
my @unsets = ();

#Configurations may be changed via the -set option
#
#  -set=name=value        : Change the default config parameter value (lowercase)\n";
#  -unset=name            : Remove the default config parameter (lowercase)\n";
#                         : Do not prepend RV_ prefex to -set/-unset variables\n";
#                         : multiple -set/-unset options accepted\n\n";
#

my $helpusage = "

Main configuration database for SWERV

This script documents, and generates the {`#} define/include files for verilog/assembly/backend flows

It is run by vsim (with defaults) every time the file changes, or when -config_set=VAR=value options are passed to vsim

This script can be run stand-alone by processes not running vsim

User options:

     -target = {default, default_mt, high_perf,  typical_pd}
        use default settings for one of the targets

     -set=var=value
        set arbitrary variable to a value
     -unset=var
        unset any definitions for var
     -snapshot=name
        name the configuration (only if no -target specified)

Parameters that can be set by the end user:

     -set=ret_stack_size =      {2, 3, 4, ... 8}
          size of return stack
     -set=btb_size =      { 32, 64, 128, 256, 512 }
          size of branch target buffer
     -set=bht_size =  {32, 64, 128, 256, 512, 1024, 2048, 4096}
          size of branch history buffer
     -set=dccm_enable = {0,1}
          DCCM enabled
     -set=dccm_num_banks = {2, 4, 8, 16}
          DCCM number of banks
     -set=dccm_region =   { 0x0, 0x1, ... 0xf }
          number of 256Mb memory region containig DCCM
     -set=dccm_offset =   hexadecimal
          offset (in bytes) of DCCM witin dccm_region
          dccm address will be: 256M * dccm_region + dccm_offset\", and that must be aligned
          to the dccm size or the next larger power of 2 if size is not a power of 2
     -set=dccm_size   =   { 4, 8, 16, 32, 48, 64, 128, 256, 512 } kB
          size of DCCM
     -set=dma_buf_depth = {2,4,5}
          DMA buffer depth
     -set=fast_interrupt_redirect =  {0, 1}
          Fast interrupt redirect mechanism
     -set=iccm_enable =   { 0, 1 }
          whether or not ICCM is enabled
     -set=icache_enable = { 0, 1 }
          whether or not icache is enabled
     -set=icache_waypack = { 0, 1 }
          whether or not icache packing is enabled
     -set=icache_ecc = { 0, 1 }
          whether or not icache has ecc - EXPENSIVE 30% sram growth
          default: icache_ecc==0 (parity)
     -set=icache_size =   { 8, 16, 32, 64, 128, 256 } kB
          size of icache
     -set=icache_num_ways { 2,4}
          Number of ways in icache
     -set=iccm_region =   { 0x0, 0x1, ... 0xf  }
          number of 256Mb memory region containing ICCM
     -set=iccm_offset =   hexadecimal
          offcet (in bytes) of ICCM within iccm_region
          iccm address will be: \"256M * iccm_region + iccm_offset\", and that must be aligned
          to the iccm size or the next larger power of 2 if size is not a power of 2
     -set=iccm_size   =   { 4 , 8 , 16 , 32, 64, 128, 256, 512 } kB
          size of ICCM
     -set=iccm_num_banks = {4,8,16}
          Number of ICCM banks
     -set=lsu_stbuf_depth = {2,4,8 }
          LSU stbuf depth
     -set=lsu_num_nbload = {2,4,8 }
          LSU number of outstanding Non Blocking loads
     -set=load_to_use_plus1 = {0 1}
          Load to use latency (fast or +1cycle)
     -set=load_to_use_bus_plus1 = {0 1}
          Load to use bus latency (fast or +1cycle)
     -set=pic_2cycle  =   { 0, 1 }
          whether or not 2-cycle PIC is enabled (2 cycle pic may result
          in an overall smaller cycle time)
     -set=pic_region =    { 0x0, 0x1, ... 0xf  }
          nuber of 256Mb memory region containing PIC memory-mapped registers
     -set=pic_offset =    hexadecial
          offset (in bytes) of PIC within pic_region
          pic address will be: \"256M * pic_region + pic_offset\", and that must be aligned
          to the pic size or the next larger power of 2 if size is not a power of 2
     -set=pic_size   =    { 32, 64, 128, 256 } kB
         size of PIC
     -set=pic_total_int = { 1, 2, 3, ..., 255 }
         number of interrupt sources in PIC
     -set=fpga_optimize = {1}
         optimize for FPGA build by disabling clock gating in lib cells 

Additionally the following may be set for bus masters and slaves using the -set=var=value option:

        lsu_bus_prty : default = 2
        lsu_bus_id   : default = 1

        ifu_bus_prty : default = 2
        ifu_bus_id   : default = 1

        sb_bus_tag   : default = 1
        sb_bus_prty  : default = 2
        sb_bus_id    : default = 1

        dma_bus_tag  : default = 1
        dma_bus_prty : default = 1
        dma_bus_id   : default = 1

        {inst,data}_access_enable[0-7] : default 0
        {inst,data}_access_addr[0-7] : default 0x00000000
        {inst,data}_access_mask[0-7] : default 0xffffffff
";


my $ret_stack_size;
my $btb_size;
my $bht_size;
my $dccm_region;
my $dccm_offset;
my $dccm_size;
my $iccm_enable;
my $icache_enable;
my $icache_waypack;
my $icache_num_ways;
my $icache_banks_way;
my $icache_ln_sz;
my $icache_bank_width;
my $icache_ecc;
my $iccm_region;
my $iccm_offset;
my $iccm_size;
my $icache_size;
my $pic_2cycle;
my $pic_region;
my $pic_offset;
my $pic_size;
my $pic_total_int;

my $top_align_iccm = 0;

my $target = "default";
my $snapshot ;
my $build_path ;
my $verbose;
my $load_to_use_plus1=0;
my $load_to_use_bus_plus1=0;
my $dccm_enable;
my $icache_2banks;
my $lsu_stbuf_depth;
my $dma_buf_depth;
my $lsu_num_nbload;
my $dccm_num_banks;
my $iccm_num_banks;
my $verilator;
my $num_threads = 1;
my $fast_interrupt_redirect = 1;                        # ON by default

$ret_stack_size=4;
$btb_size=512;
$bht_size=512;
$lsu_stbuf_depth=10;
$dccm_enable=1;
$dccm_region="0xf";
$dccm_offset="0x40000"; #1*256*1024
$dccm_size=64;
$dccm_num_banks=8;
$iccm_enable=1;
$iccm_region="0xe";
$top_align_iccm = 1;
$iccm_offset="0xe000000"; #0x380*256*1024
$iccm_size=64;
$iccm_num_banks=4;
$icache_enable=1;
$icache_waypack=0;
$icache_num_ways=4;
$icache_banks_way=2;
$icache_2banks=1;
$icache_bank_width=8;
$icache_ln_sz=64;
$icache_ecc=1;
$icache_size=32;
$pic_2cycle=1;
$pic_region="0xf";
$pic_offset="0xc0000"; # 3*256*1024
$pic_size=32;
$pic_total_int=127;
$load_to_use_plus1=0;
$fast_interrupt_redirect=1;              # default model will have fast interrupt redirects by default
$dma_buf_depth=5;
$lsu_num_nbload=8;
$num_threads=1;


GetOptions(
    "help"              => \$help,
    "target=s"          => \$target,
    "snapshot=s"        => \$snapshot,
    "verbose"           => \$verbose,
    "ret_stack_size=s"  => \$ret_stack_size,
    "btb_size=s"        => \$btb_size,
    "bht_size=s"        => \$bht_size,
    "dccm_enable=s"     => \$dccm_enable,
    "dccm_region=s"     => \$dccm_region,
    "dccm_offset=s"     => \$dccm_offset,
    "dccm_size=s"       => \$dccm_size,
    "dma_buf_depth"     => \$dma_buf_depth,
    "iccm_enable=s"     => \$iccm_enable,
    "icache_enable=s"   => \$icache_enable,
    "icache_waypack=s"   => \$icache_waypack,
    "icache_num_ways=s" => \$icache_num_ways,
    "icache_ln_sz=s"    => \$icache_ln_sz,
    "icache_ecc=s"      => \$icache_ecc,
    "icache_2banks=s"   => \$icache_2banks,
    "iccm_region=s"     => \$iccm_region,
    "iccm_offset=s"     => \$iccm_offset,
    "iccm_size=s"       => \$iccm_size,
    "lsu_stbuf_depth"   => \$lsu_stbuf_depth,
    "lsu_num_nbload"    => \$lsu_num_nbload,
    "load_to_use_plus1"   => \$load_to_use_plus1,
    "load_to_use_bus_plus1"  => \$load_to_use_bus_plus1,
    "pic_2cycle=s"      => \$pic_2cycle,
    "pic_region=s"      => \$pic_region,
    "pic_offset=s"      => \$pic_offset,
    "pic_size=s"        => \$pic_size,
    "pic_total_int=s"   => \$pic_total_int,
    "icache_size=s"     => \$icache_size,
    "set=s@"            => \@sets,
    "unset=s@"          => \@unsets,
) || die("$helpusage");

if ($help) {
   print "$helpusage\n";
   exit;
}

if (!defined $snapshot ) {
    $snapshot = $target;
}

if (!defined $ENV{BUILD_PATH}) {
    $build_path = "$ENV{RV_ROOT}/configs/snapshots/$snapshot" ;
} else {
    $build_path = $ENV{BUILD_PATH};
}

if (! -d "$build_path") {
    system ("mkdir -p $build_path");
}

# Parameter file
my $tdfile = "$build_path/eh2_pdef.vh";
my $paramfile = "$build_path/eh2_param.vh";

# Verilog defines file path
my $vlogfile = "$build_path/common_defines.vh";

# Assembly defines file path
my $asmfile = "$build_path/defines.h";

# PD defines file path
my $pdfile = "$build_path/pd_defines.vh";

# Whisper config file path
my $whisperfile = "$build_path/whisper.json";

# Perl defines file path
my $perlfile = "$build_path/perl_configs.pl";

my $no_secondary_alu=0;
my $opensource=0;

# general stuff - can't set from command line other than -set

# all mt models (num_threads==2) should have load_to_use_plus1=1;

if ($target eq "default_mt") {
    $icache_waypack=0;
    $dccm_size=128;
    $load_to_use_bus_plus1=1;
    $num_threads=2;
}
elsif ($target eq "high_perf") {
    $icache_waypack=0;
    $dccm_size=128;
    $load_to_use_bus_plus1=1;
    $num_threads=2;
    $btb_size=512;
    $bht_size=4096;
    $icache_size=32;
}
elsif ($target eq "typical_pd") {
    print "$self: Using target \"typical_pd\"\n";
    $btb_size=32;
    $bht_size=128;
    $iccm_enable=0;
    $dccm_num_banks=4;
    $icache_size=16;
    $icache_2banks=0;
    $pic_total_int=96;
    $load_to_use_bus_plus1=1;
    $num_threads=2;
}
elsif($target eq 'default'){
}
else {
    die "$self: ERROR! Unsupported target \"$target\". Supported are \"default\", \"default_mt\", \"high_perf\", \"typical_pd\", !\n";
}



# Configure triggers
our @triggers = (#{{{
    {
        "reset"         => ["0x23e00000", "0x00000000", "0x00000000"],
        "mask"          => ["0x081818c7", "0xffffffff", "0x00000000"],
        "poke_mask"     => ["0x081818c7", "0xffffffff", "0x00000000"]
    },
    {
        "reset"         => ["0x23e00000", "0x00000000", "0x00000000"],
        "mask"          => ["0x081818c7", "0xffffffff", "0x00000000"],
        "poke_mask"     => ["0x081818c7", "0xffffffff", "0x00000000"]
    },
    {
        "reset"         => ["0x23e00000", "0x00000000", "0x00000000"],
        "mask"          => ["0x081818c7", "0xffffffff", "0x00000000"],
        "poke_mask"     => ["0x081818c7", "0xffffffff", "0x00000000"]
    },
    {
        "reset"         => ["0x23e00000", "0x00000000", "0x00000000"],
        "mask"          => ["0x081818c7", "0xffffffff", "0x00000000"],
        "poke_mask"     => ["0x081818c7", "0xffffffff", "0x00000000"]
    },
 );#}}}


# Configure CSRs
our %csr = (#{{{
    "mstatus" => {
       "reset"         => "0x1800", # MPP bits hard wired to binrary 11.
       "mask"          => "0x88",   # Only mpie(7) & mie(3) bits writeable
       "exists"        => "true",
    },
    "mie" => {
        "reset"         => "0x0",
        # Only external, timer, local, and software writeable
        "mask"          => "0x70000888",
        "exists"        => "true",
    },
    "mip" => {
        "reset"         => "0x0",
        # None of the bits are writeable using CSR instructions
        "mask"          => "0x0",
        # Bits corresponding to error overflow, external, timer and stoftware
        # interrupts are modifiable
        "poke_mask"     => "0x70000888",
        "exists"        => "true",
    },
    "mcountinhibit" => {
       "commnet"       => "Performance counter inhibit. One bit per counter.",
       "reset"         => "0x0",
       "mask"          => "0x7d",
       "poke_mask"          => "0x7d",
       "exists"        => "true",
   },
  "mvendorid" => {
       "reset"         => "0x45",
       "mask"          => "0x0",
       "exists"        => "true",
   },
   "marchid" => {
       "reset"         => "0x00000011",
       "mask"          => "0x0",
       "exists"        => "true",
   },
   "mimpid" => {
       "reset"         => "0x2",
       "mask"          => "0x0",
       "exists"        => "true",
   },
   "misa" => {
       "reset"         => "0x40001105",
       "mask"          => "0x0",
       "exists"        => "true",
   },
   "tselect" => {
       "reset"         => "0x0",
       "mask"          => "0x3",    # Four triggers
       "exists"        => "true",
   },
   "dcsr" => {
       "reset"         => "0x40000003",
       "mask"          => "0x00008c04",
       "poke_mask"     => "0x00008dcc",  # cause field modifiable, nmip modifiable
       "exists"        => "true",
       "debug"         => "true",
    },
    "cycle" => {
        "exists"       => "false",
    },
    "time" => {
        "exists"       => "false",
    },
    "instret" => {
        "exists"       => "false",
    },
    "mhpmcounter3" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter4" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter5" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter6" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter3h" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter4h" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter5h" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter6h" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmevent3" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmevent4" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmevent5" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmevent6" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
# Remaining CSRs are non-standard. These are specific to SWERV
    "dicawics" => {
       "number"        => "0x7c8",
       "reset"         => "0x0",
       "mask"          => "0x0130fffc",
       "exists"        => "true",
    },
    "dicad0" => {
       "number"        => "0x7c9",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "dicad1" => {
       "number"        => "0x7ca",
       "reset"         => "0x0",
       "mask"          => "0x3",
       "exists"        => "true",
    },
    "dicago" => {
       "number"        => "0x7cb",
       "reset"         => "0x0",
       "mask"          => "0x0",
       "exists"        => "true",
    },
    "mitcnt0" => {
       "number"        => "0x7d2",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mitbnd0" => {
       "number"        => "0x7d3",
       "reset"         => "0xffffffff",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mitctl0" => {
       "number"        => "0x7d4",
       "reset"         => "0x1",
       "mask"          => "0x00000007",
       "exists"        => "true",
    },
    "mitcnt1" => {
       "number"        => "0x7d5",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mitbnd1" => {
       "number"        => "0x7d6",
       "reset"         => "0xffffffff",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mitctl1" => {
       "number"        => "0x7d7",
       "reset"         => "0x1",
       "mask"          => "0x0000000f",
       "exists"        => "true",
    },
    "mcpc" => {
       "comment"       => "Core pause",
       "number"        => "0x7c2",
       "reset"         => "0x0",
       "mask"          => "0x0",
       "exists"        => "true",
    },
     "mpmc" => {
       "number"        => "0x7c6",
       "reset"         => "0x2",
       "mask"          => "0x2",
       "exists"        => "true",
    },
    "micect" => {
       "number"        => "0x7f0",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "miccmect" => {
       "number"        => "0x7f1",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mdccmect" => {
       "number"        => "0x7f2",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mcgc" => {
       "number"        => "0x7f8",
       "reset"         => "0x0",
       "mask"          => "0x000001ff",
       "poke_mask"     => "0x000001ff",
       "exists"        => "true",
           "shared"        => "true",
    },
    "mfdc" => {
       "number"        => "0x7f9",
       "reset"         => "0x00070000",
       "mask"          => "0x00070f4d",
       "exists"        => "true",
           "shared"        => "true",
    },
    "mrac" => {
       "comment"       => "Memory region io and cache control.",
       "number"        => "0x7c0",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
       "shared"        => "true",
    },
    "dmst" => {
       "comment"       => "Memory synch trigger: Flush caches in debug mode.",
       "number"        => "0x7c4",
       "reset"         => "0x0",
       "mask"          => "0x0",
       "exists"        => "true",
       "debug"         => "true",
    },
    "dicawics" => {
       "comment"       => "Cache diagnostics.",
       "number"        => "0x7c8",
       "reset"         => "0x0",
       "mask"          => "0x0130fffc",
       "exists"        => "true",
       "debug"         => "true",
    },
    "dicad0" => {
       "comment"       => "Cache diagnostics.",
       "number"        => "0x7c9",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
       "debug"         => "true",
    },
    "dicad1" => {
       "comment"       => "Cache diagnostics.",
       "number"        => "0x7ca",
       "reset"         => "0x0",
       "mask"          => "0x3",
       "exists"        => "true",
       "debug"         => "true",
    },
    "dicago" => {
       "comment"       => "Cache diagnostics.",
       "number"        => "0x7cb",
       "reset"         => "0x0",
       "mask"          => "0x0",
       "exists"        => "true",
       "debug"         => "true",
    },
    "meipt" => {
       "comment"       => "External interrupt priority threshold.",
       "number"        => "0xbc9",
       "reset"         => "0x0",
       "mask"          => "0xf",
       "exists"        => "true",
    },
    "meicpct" => {
       "comment"       => "External claim id/priority capture.",
       "number"        => "0xbca",
       "reset"         => "0x0",
       "mask"          => "0x0",
       "exists"        => "true",
    },
    "meicidpl" => {
       "comment"       => "External interrupt claim id priority level.",
       "number"        => "0xbcb",
       "reset"         => "0x0",
       "mask"          => "0xf",
       "exists"        => "true",
    },
    "meicurpl" => {
       "comment"       => "External interrupt current priority level.",
       "number"        => "0xbcc",
       "reset"         => "0x0",
       "mask"          => "0xf",
       "exists"        => "true",
    },
    "mfdht" => {
        "comment"      => "Force Debug Halt Threshold",
        "number"       => "0x7ce",
        "reset"        => "0x0",
        "mask"         => "0x0000003f",
        "exists"       => "true",
        "shared"       => "true",
    },
    "mfdhs" => {
        "comment"      => "Force Debug Halt Status",
        "number"       => "0x7cf",
        "reset"        => "0x0",
        "mask"         => "0x00000003",
        "exists"       => "true",
    },
    "mscause" => {
        "number"       => "0x7ff",
        "reset"        => "0x0",
        "mask"         => "0x0000000f",
        "exists"       => "true",
    },
    "mhartstart" => {
        "comment"      => "Hart start mask",
        "number"       => "0x7fc",
        "reset"        => "0x1",
        "mask"         => "0x2",  # Only bit 1 is writeable
        "exists"       => "true",
        "shared"       => "true",
    },
    "mhartnum" => {
        "comment"      => "Hart count",
        "number"       => "0xfc4",
        "reset"        => "0x2",
        "mask"         => "0x0",
        "exists"       => "true",
        "shared"       => "true",
    },
    "mnmipdel" => {
        "comment"      => "NMI pin delegation",
        "number"       => "0x7fe",
        "reset"        => "0x1",
        "mask"         => "0x3",
        "exists"       => "true",
        "shared"       => "true",
    },
    "mhartid" => {
        "reset"        => "0x0",
        "mask"         => "0x0",
        "poke_mask"    => "0xfffffff0",  # Bits 0-3 not pokeable.
        "exists"       => "true",
    },

);#}}}


foreach my $i (0 .. 3) {
    $csr{"pmpcfg$i"} = { "exists" => "false" };
}

foreach my $i (0 .. 15) {
    $csr{"pmpaddr$i"} = { "exists" => "false" };
}



# }}}
# Main config hash, with default values
#
# Hash can be hierarchical with arbitrary levels
# Hexadecimal values are prefixed with 0x
#
# For verilog, if bit width is expected, add to %width hash below
our %config = (#{{{
    "harts"                 => "1",
    "xlen"                  => "32",                            # Testbench, Do Not Override
    "numiregs"              => "32",                            # Testbench, Do Not Override
    "regwidth"              => "32",                            # Testbench, Do Not Override
    "reset_vec"             => "0x80000000",                    # Testbench, Overridable
    "nmi_vec"               => "0x11110000",                    # Testbench, Overridable
    "physical"              => "1",
    "num_mmode_perf_regs"   => "4",                             # Whisper only
    "max_mmode_perf_event"  => "516",                           # Whisper only: performance counters event ids will be clamped to this
    "target"                => $target,                         # Flow Infrastructure
    "config_key"            => "derived",
    "tec_rv_icg"            => "clockhdr",

    "retstack" => {
            "ret_stack_size"    => "$ret_stack_size",           # Design Parm, Overridable
    },

    "btb"  => {
         "btb_size"         => "$btb_size",                     # Design Parm, Overridable
         "btb_index1_hi"    => "derived",
         "btb_index1_lo"    => "3",                             # Constant, Do Not Override
         "btb_index2_hi"    => "derived",
         "btb_index2_lo"    => "derived",
         "btb_index3_hi"    => "derived",
         "btb_index3_lo"    => "derived",
         "btb_addr_hi"      => "derived",
         "btb_array_depth"  => "derived",
         "btb_addr_lo"      => "3",                             # Constant, Do Not Override
         "btb_btag_size"    => "derived",
    },
    "bht"  => {
         "bht_size"         => "$bht_size",                     # Design Parm, Overridable
         "bht_addr_hi"      => "derived",
         "bht_addr_lo"      => "3",                             # Constant, Do Not Override
         "bht_array_depth"  => "derived",
         "bht_ghr_size"     => "derived",
         "bht_ghr_hash_1"   => "derived",
         "bht_ghr_range"    => "derived",
         "bht_ghr_pad"      => "derived",
         "bht_ghr_pad2"     => "derived",
         "bht_hash_string"  => "derived",

    },

    "core" => {
        "timer_legal_en"     => '1',                            # Design Parm, Overridable
        "lsu_stbuf_depth"    => "$lsu_stbuf_depth",             # Design Parm, Overridable
        "dma_buf_depth"      => "$dma_buf_depth",               # Design Parm, Overridable
        "lsu_num_nbload"     => "$lsu_num_nbload",              # Design Parm, Overridable
        "opensource"         => "$opensource",                  # Flow Infrastructure
        "verilator"          => "$verilator",                   # Flow Infrastructure
        "no_secondary_alu"   => "$no_secondary_alu",            # Design Parm, Overridable
        "load_to_use_plus1"  => "$load_to_use_plus1",           # Design Parm, Overridable
        "load_to_use_bus_plus1"  => "$load_to_use_bus_plus1",   # Design Parm, Overridable
        "iccm_icache"        => 'derived',                      # Used by design
        "iccm_only"          => 'derived',                      # Used by design
        "icache_only"        => 'derived',                      # Used by design
        "no_iccm_no_icache"  => 'derived',                      # Used by design
        "num_threads"        => "$num_threads",                 # Design Parm, Overridable
        "fpga_optimize"      => "0",                            # Optimize fpga speed by removing clock gating
        "fast_interrupt_redirect"  => "$fast_interrupt_redirect"# Design Parm, Overridable

    },

    "dccm" => {
        "dccm_enable"       => "$dccm_enable",                  # Design Parm, Overridable
        "dccm_region"       => "$dccm_region",                  # Design Parm, Overridable
        "dccm_offset"       => "$dccm_offset",                  # Design Parm, Overridable
        "dccm_size"         => "$dccm_size",                    # Design Parm, Overridable
        "dccm_num_banks"    => "$dccm_num_banks",               # Design Parm, Overridable
        "dccm_sadr"         => 'derived',
        "dccm_eadr"         => 'derived',
        "dccm_bits"         => 'derived',
        "dccm_bank_bits"    => 'derived',
        "dccm_data_width"   => 'derived',
        "dccm_fdata_width"  => 'derived',
        "dccm_byte_width"   => 'derived',
        "dccm_width_bits"   => 'derived',
        "dccm_index_bits"   => 'derived',
        "dccm_ecc_width"    => 'derived',
        "lsu_sb_bits"       => 'derived',
        "dccm_data_cell"    => 'derived',
        "dccm_rows"         => 'derived',
        "dccm_reserved"     => 'derived',                      # Testbench use only: reserve dccm space for SW/stack - no random r/w
    },


    "iccm" => {
        "iccm_enable"         => "$iccm_enable",               # Design Parm, Overridable
        "iccm_region"         => "$iccm_region",               # Design Parm, Overridable
        "iccm_offset"         => "$iccm_offset",               # Design Parm, Overridable
        "iccm_size"           => "$iccm_size",                 # Design Parm, Overridable
        "iccm_num_banks"      => "$iccm_num_banks",            # Design Parm, Overridable
        "iccm_bank_bits"      => 'derived',
        "iccm_index_bits"     => 'derived',
        "iccm_rows"           => 'derived',
        "iccm_data_cell"      => 'derived',
        "iccm_sadr"           => 'derived',
        "iccm_eadr"           => 'derived',
        "iccm_reserved"       => 'derived',                    # Testbench use only : reserve iccm space for SW/handlers - no random r/w
        "iccm_bank_hi"        => 'derived',
        "iccm_bank_index_lo"  => 'derived',
    },
    "icache" => {
        "icache_enable"         => "$icache_enable",           # Design Parm, Overridable
        "icache_waypack"        => "$icache_waypack",          # Design Parm, Overridable
        "icache_num_ways"       => "$icache_num_ways",         # Design Parm, Overridable
        "icache_banks_way"      => "2",                        # Design Parm, Constant
        "icache_bank_width"     => "8",                        # Design Parm, Constant
        "icache_ln_sz"          => "$icache_ln_sz",            # Design Parm, Overridable
        "icache_size"           => "$icache_size",             # Design Parm, Overridable
        "icache_bank_hi"        => 'derived',
        "icache_bank_lo"        => 'derived',
        "icache_data_cell"      => 'derived',
        "icache_tag_cell"       => 'derived',
        "icache_tag_depth"      => 'derived',
        "icache_data_depth"     => 'derived',
        "icache_num_lines"      => 'derived',
        "icache_num_lines_bank" => 'derived',
        "icache_num_lines_way"  => 'derived',
        "icache_data_depth"     => 'derived',
        "icache_tag_lo"         => 'derived',
        "icache_ecc"             => "$icache_ecc",              # Design Parm, Overridable
        "icache_index_hi"        => 'derived',
        "icache_data_index_lo"   => 'derived',
        "icache_data_width"      => 'derived',
        "icache_fdata_width"     => 'derived',
        "icache_tag_index_lo"    => 'derived',
        "icache_2banks"          => "$icache_2banks",           # Design Parm, Overridable
        "icache_bank_bits"       => "derived",
        "icache_status_bits"     => "derived",
        "icache_num_beats"       => "derived",
        "icache_beat_bits"       => "derived",
        "icache_scnd_last"       => "derived",
        "icache_beat_addr_hi"    => "derived",
    },
    "pic" => {
        "pic_2cycle"           => "$pic_2cycle",                # Design Parm, Overridable
        "pic_region"           => "$pic_region",                # Design Parm, Overridable
        "pic_offset"           => "$pic_offset",                # Design Parm, Overridable
        "pic_size"             => "$pic_size",                  # Design Parm, Overridable
        "pic_base_addr"        => 'derived',                    # base_addr = pic_region + offset
        "pic_total_int_plus1"  => 'derived',                    # # Testbench only
        "pic_total_int"        => "$pic_total_int",             # Design Parm, Overridable
        "pic_int_words"        => 'derived',                    # number of 32 bit words for packed registers (Xmax)
        "pic_bits"             => 'derived',                    # of bits needs to address the PICM
        "pic_meipl_offset"     => '0x0000',                     # Testbench only: Offset of meipl relative to pic_base_addr
        "pic_meip_offset"      => '0x1000',                     # Testbench only: Offset of meip relative to pic_base_addr
        "pic_meitp_offset"     => '0x1800',                     # Testbench only: Offset of meitp relative to pic_base_addr
        "pic_meie_offset"      => '0x2000',                     # Testbench only: Offset of meie relative to pic_base_addr
        "pic_mpiccfg_offset"   => '0x3000',                     # Testbench only: Offset of mpiccfg relative to pic_base_addr
        "pic_meipt_offset"     => '0x3004',                     # Testbench only: Offset of meipt relative to pic_base_addr -- deprecated
        "pic_meigwctrl_offset" => '0x4000',                     # Testbench only: gateway control regs relative to pic_base_addr
        "pic_meigwclr_offset"  => '0x5000',                     # Testbench only: gateway clear regs relative to pic_base_addr
        "pic_meidels_offset"   => '0x6000',                     # Testbench only: external interrupt delegation register

        "pic_meipl_mask"     => '0xf',
        "pic_meip_mask"      => '0x0',
        "pic_meitp_mask"     => '0x0',
        "pic_meie_mask"      => '0x1',
        "pic_mpiccfg_mask"   => '0x1',
        "pic_meipt_mask"     => '0x0',
        "pic_meigwctrl_mask" => '0x3',
        "pic_meigwclr_mask"  => '0x0',
        "pic_meidels_mask"   => '0x1',

        "pic_meipl_count"     => $pic_total_int,
        "pic_meip_count"      => 4,
        "pic_meitp_count"     => $pic_total_int,
        "pic_meie_count"      => $pic_total_int,
        "pic_mpiccfg_count"   => 1,
        "pic_meipt_count"     => $pic_total_int,
        "pic_meigwctrl_count" => $pic_total_int,
        "pic_meigwclr_count"  => $pic_total_int,
        "pic_meidels_count"   => $pic_total_int
    },
    "testbench" => {
        "TOP"               => "tb_top",
        "RV_TOP"            => "`TOP.rvtop",
        "CPU_TOP"           => "`RV_TOP.swerv",
        "clock_period"      => "100",
        "build_ahb_lite"    => "0",
        "build_axi4"        => "1",
        "build_axi_native"  => "1",
        "assert_on"         => "",
        "datawidth"         => "64",
        "ext_datawidth"     => "64",
        "ext_addrwidth"     => "32",
        "sterr_rollback"    => "0",
        "lderr_rollback"    => "1",
        "SDVT_AHB"          => "1",
        "mt_build"          => "0",
    },
    "protection" => {                                           # Design parms, Overridable
        "inst_access_enable0" => "0x0",
        "inst_access_addr0"   => "0x00000000",
        "inst_access_mask0"   => "0xffffffff",
        "inst_access_enable1" => "0x0",
        "inst_access_addr1"   => "0x00000000",
        "inst_access_mask1"   => "0xffffffff",
        "inst_access_enable2" => "0x0",
        "inst_access_addr2"   => "0x00000000",
        "inst_access_mask2"   => "0xffffffff",
        "inst_access_enable3" => "0x0",
        "inst_access_addr3"   => "0x00000000",
        "inst_access_mask3"   => "0xffffffff",
        "inst_access_enable4" => "0x0",
        "inst_access_addr4"   => "0x00000000",
        "inst_access_mask4"   => "0xffffffff",
        "inst_access_enable5" => "0x0",
        "inst_access_addr5"   => "0x00000000",
        "inst_access_mask5"   => "0xffffffff",
        "inst_access_enable6" => "0x0",
        "inst_access_addr6"   => "0x00000000",
        "inst_access_mask6"   => "0xffffffff",
        "inst_access_enable7" => "0x0",
        "inst_access_addr7"   => "0x00000000",
        "inst_access_mask7"   => "0xffffffff",
        "data_access_enable0" => "0x0",
        "data_access_addr0"   => "0x00000000",
        "data_access_mask0"   => "0xffffffff",
        "data_access_enable1" => "0x0",
        "data_access_addr1"   => "0x00000000",
        "data_access_mask1"   => "0xffffffff",
        "data_access_enable2" => "0x0",
        "data_access_addr2"   => "0x00000000",
        "data_access_mask2"   => "0xffffffff",
        "data_access_enable3" => "0x0",
        "data_access_addr3"   => "0x00000000",
        "data_access_mask3"   => "0xffffffff",
        "data_access_enable4" => "0x0",
        "data_access_addr4"   => "0x00000000",
        "data_access_mask4"   => "0xffffffff",
        "data_access_enable5" => "0x0",
        "data_access_addr5"   => "0x00000000",
        "data_access_mask5"   => "0xffffffff",
        "data_access_enable6" => "0x0",
        "data_access_addr6"   => "0x00000000",
        "data_access_mask6"   => "0xffffffff",
        "data_access_enable7" => "0x0",
        "data_access_addr7"   => "0x00000000",
        "data_access_mask7"   => "0xffffffff",
     },
    "memmap" => {
        "serialio"          => 'derived, overridable',          # Testbench only
        "external_data"     => 'derived, overridable',          # Testbench only
        "external_prog"     => 'derived, overridable',          # Testbench only
        "debug_sb_mem"      => 'derived, overridable',          # Testbench only
        "external_data_1"   => 'derived, overridable',          # Testbench only
        "external_mem_hole" => 'derived, overridable',          # Testbench only
    },
    "bus" => {
        "lsu_bus_tag"      => '4',                              # Design parm, Constant
        "lsu_bus_id"       => '1',                              # Design parm, Overridable
        "lsu_bus_prty"     => '2',                              # Design parm, Overridable
        "dma_bus_tag"      => '1',                              # Design parm, Overridable
        "dma_bus_id"       => '1',                              # Design parm, Overridable
        "dma_bus_prty"     => '2',                              # Design parm, Overridable
        "sb_bus_tag"       => '1',                              # Design parm, Overridable
        "sb_bus_id"        => '1',                              # Design parm, Overridable
        "sb_bus_prty"      => '2',                              # Design parm, Overridable
        "ifu_bus_tag"      => '4',                              # Design parm, Constant
        "ifu_bus_id"       => '1',                              # Design parm, Overridable
        "ifu_bus_prty"     => '2',                              # Design parm, Overridable
        "bus_prty_default"     => '3',
    },
    "triggers" => \@triggers,                                   # Whisper only
    "csr" => \%csr,                                             # Whisper only
    "even_odd_trigger_chains" => "true",                        # Whisper only
);

# These parms are used in the verilog and will be part of global parm structure
# need to have this be width in binary
# for now autosize to the data
our %verilog_parms = (
        "timer_legal_en"          => '1',
        "num_threads"             => '2',
        "fast_interrupt_redirect" => '1',
        "inst_access_enable0"     => '1',
        "inst_access_addr0"       => '32',
        "inst_access_mask0"       => '32',
        "inst_access_enable1"     => '1',
        "inst_access_addr1"       => '32',
        "inst_access_mask1"       => '32',
        "inst_access_enable2"     => '1',
        "inst_access_addr2"       => '32',
        "inst_access_mask2"       => '32',
        "inst_access_enable3"     => '1',
        "inst_access_addr3"       => '32',
        "inst_access_mask3"       => '32',
        "inst_access_enable4"     => '1',
        "inst_access_addr4"       => '32',
        "inst_access_mask4"       => '32',
        "inst_access_enable5"     => '1',
        "inst_access_addr5"       => '32',
        "inst_access_mask5"       => '32',
        "inst_access_enable6"     => '1',
        "inst_access_addr6"       => '32',
        "inst_access_mask6"       => '32',
        "inst_access_enable7"     => '1',
        "inst_access_addr7"       => '32',
        "inst_access_mask7"       => '32',
        "data_access_enable0"     => '1',
        "data_access_addr0"       => '32',
        "data_access_mask0"       => '32',
        "data_access_enable1"     => '1',
        "data_access_addr1"       => '32',
        "data_access_mask1"       => '32',
        "data_access_enable2"     => '1',
        "data_access_addr2"       => '32',
        "data_access_mask2"       => '32',
        "data_access_enable3"     => '1',
        "data_access_addr3"       => '32',
        "data_access_mask3"       => '32',
        "data_access_enable4"     => '1',
        "data_access_addr4"       => '32',
        "data_access_mask4"       => '32',
        "data_access_enable5"     => '1',
        "data_access_addr5"       => '32',
        "data_access_mask5"       => '32',
        "data_access_enable6"     => '1',
        "data_access_addr6"       => '32',
        "data_access_mask6"       => '32',
        "data_access_enable7"     => '1',
        "data_access_addr7"       => '32',
        "data_access_mask7"       => '32',
        "iccm_bits"               => '5',
        "iccm_bank_hi"            => '5',
        "iccm_bank_index_lo"      => '5',
        "icache_bank_bits"        => '3',
        "icache_status_bits"      => '3',
        "icache_num_beats"        => '4',
        "icache_beat_bits"        => '4',
        "icache_scnd_last"        => '4',
        "icache_beat_addr_hi"     => '4',
        "iccm_icache"             => '1',
        "iccm_only"               => '1',
        "icache_only"             => '1',
        "no_iccm_no_icache"       => '1',
        "build_svc"               => '1',
        "build_axi4"              => '1',
        "build_ahb_lite"          => '1',
        "build_axi_native"        => '1',
        "lsu_num_nbload_width"    => '3',
        "lsu_num_nbload"          => '5',
        "ret_stack_size"          => '4',
        "btb_size"                => '10',
        "btb_index1_hi"           => '5',
        "btb_index1_lo"           => '5',
        "btb_index2_hi"           => '5',
        "btb_index2_lo"           => '5',
        "btb_index3_hi"           => '5',
        "btb_index3_lo"           => '5',
        "btb_addr_hi"             => '5',
        "btb_array_depth"         => '9',
        "btb_addr_lo"             => '2',
        "btb_btag_size"           => '4',
        "btb_btag_fold"           => '1',
        "btb_fold2_index_hash"    => '1',
        "bht_size"                => '13',
        "bht_addr_hi"             => '4',
        "bht_addr_lo"             => '2',
        "bht_array_depth"         => '12',
        "bht_ghr_size"            => '4',
        "bht_ghr_hash_1"          => '1',
        "lsu_stbuf_depth"         => '4',
        "dma_buf_depth"           => '3',
        "no_secondary_alu"        => '1',
        "load_to_use_plus1"       => '1',
        "load_to_use_bus_plus1"   => '1',
        "dccm_enable"             => '1',
        "dccm_region"             => '4',
        "dccm_size"               => '10',
        "dccm_num_banks"          => '5',
        "dccm_sadr"               => '32',
        "dccm_bits"               => '5',
        "dccm_bank_bits"          => '3',
        "dccm_data_width"         => '6',
        "dccm_fdata_width"        => '6',
        "dccm_byte_width"         => '3',
        "dccm_width_bits"         => '2',
        "dccm_index_bits"         => '4',
        "dccm_ecc_width"          => '3',
        "lsu_sb_bits"             => '5',
        "iccm_enable"             => '1',
        "iccm_region"             => '4',
        "iccm_size"               => '10',
        "iccm_num_banks"          => '5',
        "iccm_bank_bits"          => '3',
        "iccm_index_bits"         => '4',
        "iccm_sadr"               => '32',
        "icache_enable"           => '1',
        "icache_waypack"          => '1',
        "icache_num_ways"         => '3',
        "icache_banks_way"        => '3',
        "icache_bank_width"       => '4',
        "icache_ln_sz"            => '7',
        "icache_size"             => '9',
        "icache_bank_hi"          => '3',
        "icache_bank_lo"          => '2',
        "icache_tag_depth"        => '13',
        "icache_data_depth"       => '14',
        "icache_tag_lo"           => '5',
        "icache_index_hi"         => '5',
        "icache_data_index_lo"    => '3',
        "icache_data_width"       => '7',
        "icache_fdata_width"      => '7',
        "icache_tag_index_lo"     => '3',
        "icache_ecc"              => '1',
        "icache_2banks"           => '1',
        "pic_2cycle"              => '1',
        "pic_region"              => '4',
        "pic_size"                => '9',
        "pic_base_addr"           => '32',
        "pic_total_int_plus1"     => '9',
        "pic_total_int"           => '8',
        "pic_int_words"           => '4',
        "pic_bits"                => '5',
        "lsu_bus_tag"             => '4',
        "lsu_bus_id"              => '1',
        "lsu_bus_prty"            => '2',
        "dma_bus_tag"             => '4',
        "dma_bus_id"              => '1',
        "dma_bus_prty"            => '2',
        "sb_bus_tag"              => '4',
        "sb_bus_id"               => '1',
        "sb_bus_prty"             => '2',
        "ifu_bus_tag"             => '4',
        "ifu_bus_id"              => '1',
        "ifu_bus_prty"            => '2',
        "bus_prty_default"        => '2',
);



# move deletes lower

# Perform any overrides first before derived values
map_set_unset();
gen_define("","", \%config,"",[]);
print "\nSweRV configuration for target=$target\n\n";
dump_define("","", \%config,[]);

# perform final checks
my $c;
$c=$config{retstack}{ret_stack_size}; if (!($c >=2 && $c <=8))                                 { die("$helpusage\n\nFAIL: ret_stack_size == $c;   ILLEGAL !!!\n\n"); }
$c=$config{btb}{btb_size};            if (!($c==32||$c==64||$c==128||$c==256||$c==512)){ die("$helpusage\n\nFAIL: btb_size == $c;         ILLEGAL !!!\n\n"); }
$c=$config{iccm}{iccm_region};        if (!($c>=0 && $c<16))                                   { die("$helpusage\n\nFAIL: iccm_region == $c       ILLEGAL !!!\n\n"); }
$c=$config{iccm}{iccm_offset};        if (!($c>=0  && $c<256*1024*1024 && ($c&0xfff)==0))      { die("$helpusage\n\nFAIL: iccm_offset == $c       ILLEGAL !!!\n\n"); }
$c=$config{iccm}{iccm_size};          if (!($c==4||$c==8||$c==16||$c==32||$c==64||$c==128||$c==256||$c==512)) { die("$helpusage\n\nFAIL: iccm_size == $c         ILLEGAL !!!\n\n"); }
$c=$config{iccm}{iccm_num_banks};     if (!( $c==4 || $c==8  || ($c==16 && $config{iccm}{iccm_size} != 4)))    { die("$helpusage\n\nFAIL: iccm_num_banks == $c    ILLEGAL !!!\n\n"); }
$c=$config{iccm}{iccm_enable};        if (!($c==0  || $c==1))                                  { die("$helpusage\n\nFAIL: iccm_enable == $c       ILLEGAL !!!\n\n"); }
$c=$config{dccm}{dccm_region};        if (!($c>=0  && $c<16))                                  { die("$helpusage\n\nFAIL: dccm_region == $c       ILLEGAL !!!\n\n"); }
$c=$config{dccm}{dccm_num_banks};     if (!($c==2 || $c==4 || $c==8 || ($c==16 && $config{dccm}{dccm_size} != 4) ))                                   { die("$helpusage\n\nFAIL: dccm_num_banks == $c    ILLEGAL !!!\n\n"); }
$c=$config{dccm}{dccm_offset};        if (!($c>=0  && $c<256*1024*1024 && ($c&0xfff)==0))      { die("$helpusage\n\nFAIL: dccm_offset == $c       ILLEGAL !!!\n\n"); }
$c=$config{dccm}{dccm_size};          if (!($c==4||$c==8||$c==16||$c==32||$c==48||$c==64||$c==128||$c==256||$c==512))        { die("$helpusage\n\nFAIL: dccm_size == $c         ILLEGAL !!!\n\n"); }
$c=$config{pic}{pic_2cycle};          if (!($c==0  || $c==1))                                  { die("$helpusage\n\nFAIL: pic_2cycle == $c        ILLEGAL !!!\n\n"); }
$c=$config{pic}{pic_region};          if (!($c>=0 && $c<16))                                   { die("$helpusage\n\nFAIL: pic_region == $c        ILLEGAL !!!\n\n"); }
$c=$config{pic}{pic_offset};          if (!($c>=0  && $c<256*1024*1024 && ($c&0xfff)==0))      { die("$helpusage\n\nFAIL: pic_offset == $c        ILLEGAL !!!\n\n"); }
$c=$config{pic}{pic_size};            if (!($c==32 || $c==64 || $c==128 || $c==256))           { die("$helpusage\n\nFAIL: pic_size == $c          ILLEGAL !!!\n\n"); }
$c=$config{pic}{pic_total_int};       if (  $c<1 || $c>255)                                    { die("$helpusage\n\nFAIL: pic_total_int == $c     ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_enable};    if (!($c==0 || $c==1))                                   { die("$helpusage\n\nFAIL: icache_enable == $c     ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_waypack};   if (!($c==0 || $c==1))                                   { die("$helpusage\n\nFAIL: icache_waypack == $c    ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_num_ways};  if (!($c==2 || $c==4))                                   { die("$helpusage\n\nFAIL: icache_num_ways == $c   ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_ln_sz};     if (!($c==32 || $c==64))                                 { die("$helpusage\n\nFAIL: icache_ln_sz == $c   ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_size};      if (!($c==8 || $c==16 || $c==32 || $c==64 || $c==128 || $c==256)) { die("$helpusage\n\nFAIL: icache_size == $c       ILLEGAL !!!\n\n"); }
$c=$config{core}{lsu_stbuf_depth};    if (!($c==2 || $c==4 || $c==8 || $c==10 ))               { die("$helpusage\n\nFAIL: lsu_stbuf_depth == $c   ILLEGAL !!!\n\n"); }
$c=$config{core}{dma_buf_depth};      if (!($c==2 || $c==4 || $c==5))                          { die("$helpusage\n\nFAIL: dma_buf_depth == $c     ILLEGAL !!!\n\n"); }
$c=$config{core}{lsu_num_nbload};     if (!($c==2 || $c==4 || $c==8))                          { die("$helpusage\n\nFAIL: lsu_num_nbload == $c   ILLEGAL !!!\n\n"); }

$c=$config{protection}{inst_access_addr0}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: inst_access_addr0 lower 6b must be 0s $c !!!\n\n"); }
$c=$config{protection}{inst_access_addr1}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: inst_access_addr1 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_addr2}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: inst_access_addr2 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_addr3}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: inst_access_addr3 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_addr4}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: inst_access_addr4 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_addr5}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: inst_access_addr5 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_addr6}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: inst_access_addr6 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_addr7}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: inst_access_addr7 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_mask0}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: inst_access_mask0 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask1}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: inst_access_mask1 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask2}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: inst_access_mask2 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask3}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: inst_access_mask3 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask4}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: inst_access_mask4 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask5}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: inst_access_mask5 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask6}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: inst_access_mask6 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask7}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: inst_access_mask7 invalid !!!\n\n"); }
$c=$config{protection}{data_access_addr0}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: data_access_addr0 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr1}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: data_access_addr1 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr2}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: data_access_addr2 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr3}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: data_access_addr3 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr4}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: data_access_addr4 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr5}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: data_access_addr5 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr6}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: data_access_addr6 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr7}; if ((hex($c)&0x3f) != 0)                            { die("$helpusage\n\nFAIL: data_access_addr7 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_mask0}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: data_access_mask0 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask1}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: data_access_mask1 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask2}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: data_access_mask2 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask3}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: data_access_mask3 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask4}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: data_access_mask4 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask5}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: data_access_mask5 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask6}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: data_access_mask6 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask7}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))      { die("$helpusage\n\nFAIL: data_access_mask7 invalid !!!\n\n"); }

if ((hex($config{protection}{inst_access_addr0}) & hex($config{protection}{inst_access_mask0}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr0 and inst_access_mask0 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr1}) & hex($config{protection}{inst_access_mask1}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr1 and inst_access_mask1 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr2}) & hex($config{protection}{inst_access_mask2}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr2 and inst_access_mask2 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr3}) & hex($config{protection}{inst_access_mask3}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr3 and inst_access_mask3 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr4}) & hex($config{protection}{inst_access_mask4}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr4 and inst_access_mask4 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr5}) & hex($config{protection}{inst_access_mask5}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr5 and inst_access_mask5 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr6}) & hex($config{protection}{inst_access_mask6}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr6 and inst_access_mask6 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr7}) & hex($config{protection}{inst_access_mask7}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr7 and inst_access_mask7 must be orthogonal!!!\n\n"); }

if ((hex($config{protection}{data_access_addr0}) & hex($config{protection}{data_access_mask0}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr0 and data_access_mask0 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr1}) & hex($config{protection}{data_access_mask1}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr1 and data_access_mask1 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr2}) & hex($config{protection}{data_access_mask2}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr2 and data_access_mask2 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr3}) & hex($config{protection}{data_access_mask3}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr3 and data_access_mask3 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr4}) & hex($config{protection}{data_access_mask4}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr4 and data_access_mask4 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr5}) & hex($config{protection}{data_access_mask5}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr5 and data_access_mask5 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr6}) & hex($config{protection}{data_access_mask6}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr6 and data_access_mask6 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr7}) & hex($config{protection}{data_access_mask7}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr7 and data_access_mask7 must be orthogonal!!!\n\n"); }

if ($config{bus}{lsu_bus_id} < 1) {die "$self : ERROR! lsu_bus_id cannot be less than 1\n"}
if ($config{bus}{dma_bus_tag} < 1) {die "$self : ERROR! dma_bus_tag cannot be less than 1\n"}
if ($config{bus}{dma_bus_id} < 1) {die "$self : ERROR! dma_bus_id cannot be less than 1\n"}
if ($config{bus}{sb_bus_tag} < 1) {die "$self : ERROR! sb_bus_tag cannot be less than 1\n"}
if ($config{bus}{sb_bus_id} < 1) {die "$self : ERROR! sb_bus_id cannot be less than 1\n"}
if ($config{bus}{ifu_bus_id} < 1) {die "$self : ERROR! ifu_bus_id cannot be less than 1\n"}
if ($config{bus}{ifu_bus_prty} < 2) {die "$self : ERROR! ifu_bus_prty cannot be less than 2\n"}


if (!grep(/^$config{bht}{bht_size}$/, qw (32 64 128 256 512 1024 2048 4096))) {die "$self : ERROR! bht_size mus be one of 32 64 128 256 512 1024 2048 4096\n"}

# Fill in derived configuration entries.

if ($config{icache}{icache_enable}==0 && $config{iccm}{iccm_enable}==0) {
    $config{core}{no_iccm_no_icache}=1;
}
elsif ($config{icache}{icache_enable}==0 && $config{iccm}{iccm_enable}==1) {
    $config{core}{iccm_only}=1;
}
elsif ($config{icache}{icache_enable}==1 && $config{iccm}{iccm_enable}==0) {
    $config{core}{icache_only}=1;
}
elsif ($config{icache}{icache_enable}==1 && $config{iccm}{iccm_enable}==1) {
    $config{core}{iccm_icache}=1;
}

$config{btb}{btb_btag_fold} = 0;
$config{btb}{btb_fold2_index_hash} = 0;


if($config{btb}{btb_size}==512){
    $config{btb}{btb_index1_hi} = 9;
    $config{btb}{btb_index2_hi} = 16;
    $config{btb}{btb_index3_hi} = 23;
    $config{btb}{btb_array_depth}= 128;
    $config{btb}{btb_btag_size} = 5;
} elsif($config{btb}{btb_size}==256){
    $config{btb}{btb_index1_hi} = 8;
    $config{btb}{btb_index2_hi} = 14;
    $config{btb}{btb_index3_hi} = 20;
    $config{btb}{btb_array_depth}= 64;
    $config{btb}{btb_btag_size} = 6;
} elsif($config{btb}{btb_size}==128){
    $config{btb}{btb_index1_hi} = 7;
    $config{btb}{btb_index2_hi} = 12;
    $config{btb}{btb_index3_hi} = 17;
    $config{btb}{btb_array_depth}= 32;
    $config{btb}{btb_btag_size} = 7;
} elsif($config{btb}{btb_size}==64){
    $config{btb}{btb_index1_hi} = 6;
    $config{btb}{btb_index2_hi} = 10;
    $config{btb}{btb_index3_hi} = 14;
    $config{btb}{btb_array_depth}= 16;
    $config{btb}{btb_btag_size} = 8;
} elsif($config{btb}{btb_size}==48){
    $config{btb}{btb_index1_hi} = 5;
    $config{btb}{btb_index2_hi} = 8;
    $config{btb}{btb_index3_hi} = 11;
    $config{btb}{btb_array_depth}= 8;
    $config{btb}{btb_48}= 1;
    $config{btb}{btb_fold2_index_hash} = 1;
    $config{btb}{btb_btag_size} = 9;
    $config{btb}{btb_btag_fold} = 1;
} elsif($config{btb}{btb_size}==32){
    $config{btb}{btb_index1_hi} = 5;
    $config{btb}{btb_index2_hi} = 8;
    $config{btb}{btb_index3_hi} = 11;
    $config{btb}{btb_array_depth}= 8;
    $config{btb}{btb_btag_size} = 9;
    $config{btb}{btb_btag_fold} = 1;
}

$config{btb}{btb_index2_lo} = $config{btb}{btb_index1_hi}+1;
$config{btb}{btb_index3_lo} = $config{btb}{btb_index2_hi}+1;
$config{btb}{btb_addr_hi}   = $config{btb}{btb_index1_hi};



# BHT index is a hash of the GHR and PC_HASH
sub ghrhash{
    my($btb_index_hi,$ghr_size) = @_;
    my $ghr_hi;
    my $ghr_lo;
    my $ghr_start;
    my $string;

    $btb_size = $btb_index_hi - 2;

    $ghr_hi = $ghr_size - 1;
    $ghr_lo = $btb_size;

    $ghr_start = "{";
    if($ghr_size > $btb_size){
            $string = "{ghr[$ghr_hi:$ghr_lo], hashin[$btb_index_hi:3]^ghr[$ghr_lo-1:0]} // cf1";
    }
    else{
        $string = "{hashin[$ghr_size+2:5]^ghr[$ghr_size-1:2], ghr[1:0]}// cf2";
    }

    return $string;

}


if($config{bht}{bht_size}==4096){
    $config{bht}{bht_ghr_size}= 10;
    $config{bht}{bht_ghr_range}= "9:0";
    $config{bht}{bht_ghr_pad}= "fghr[5:0],3'b0";
    $config{bht}{bht_ghr_pad2}= "fghr[6:0],2'b0";
    $config{bht}{bht_array_depth}= 1024;
    $config{bht}{bht_addr_hi}= 12;
}elsif($config{bht}{bht_size}==2048){
    $config{bht}{bht_ghr_size}= 9;
    $config{bht}{bht_ghr_range}= "8:0";
    $config{bht}{bht_ghr_pad}= "fghr[4:0],3'b0";
    $config{bht}{bht_ghr_pad2}= "fghr[5:0],2'b0";
    $config{bht}{bht_array_depth}= 512;
    $config{bht}{bht_addr_hi}= 11;
} elsif($config{bht}{bht_size}==1024){
    $config{bht}{bht_ghr_size}= 8;
    $config{bht}{bht_ghr_range}= "7:0";
    $config{bht}{bht_ghr_pad}= "fghr[3:0],3'b0";
    $config{bht}{bht_ghr_pad2}= "fghr[4:0],2'b0";
    $config{bht}{bht_array_depth}= 256;
    $config{bht}{bht_addr_hi}= 10;
} elsif($config{bht}{bht_size}==512){
    $config{bht}{bht_ghr_size}= 7;
    $config{bht}{bht_ghr_range}= "6:0";
    $config{bht}{bht_ghr_pad}= "fghr[2:0],3'b0";
    $config{bht}{bht_ghr_pad2}= "fghr[3:0],2'b0";
    $config{bht}{bht_array_depth}= 128;
    $config{bht}{bht_addr_hi}= 9;
} elsif($config{bht}{bht_size}==256){
    $config{bht}{bht_ghr_size}= 6;
    $config{bht}{bht_ghr_range}= "5:0";
    $config{bht}{bht_ghr_pad}= "fghr[1:0],3'b0";
    $config{bht}{bht_ghr_pad2}= "fghr[2:0],2'b0";
    $config{bht}{bht_addr_hi} = 8;
    $config{bht}{bht_array_depth}= 64;
} elsif($config{bht}{bht_size}==128){
    $config{bht}{bht_ghr_size}= 5;
    $config{bht}{bht_ghr_range}= "4:0";
    $config{bht}{bht_ghr_pad}= "fghr[0],3'b0";
    $config{bht}{bht_ghr_pad2}= "fghr[1:0],2'b0";
    $config{bht}{bht_addr_hi} = 7;
    $config{bht}{bht_array_depth}= 32;
} elsif($config{bht}{bht_size}==64){
    $config{bht}{bht_ghr_size}= 4;
    $config{bht}{bht_ghr_range}= "3:0";
    $config{bht}{bht_ghr_pad}= "3'b0 ";
    $config{bht}{bht_ghr_pad2}= "fghr[0],2'b0";
    $config{bht}{bht_addr_hi} = 6;
    $config{bht}{bht_array_depth}= 16;
} elsif($config{bht}{bht_size}==32){
    $config{bht}{bht_ghr_size}= 3;
    $config{bht}{bht_ghr_range}= "2:0";
    $config{bht}{bht_ghr_pad}= "2'b0 ";
    $config{bht}{bht_ghr_pad2}= "2'b0";
    $config{bht}{bht_addr_hi} = 5;
    $config{bht}{bht_array_depth}= 8;
}
#if($config{bht}{bht_size}==2048){
#    $config{bht}{bht_ghr_size}= 8;
#    $config{bht}{bht_ghr_range}= "7:0";
#    $config{bht}{bht_ghr_pad}= "fghr[7:4],3'b0";
#    $config{bht}{bht_ghr_pad2}= "fghr[7:3],2'b0";
#    $config{bht}{bht_array_depth}= 256;
#    $config{bht}{bht_addr_hi}= 11;
#} elsif($config{bht}{bht_size}==1024){
#    $config{bht}{bht_ghr_size}= 7;
#    $config{bht}{bht_ghr_range}= "6:0";
#    $config{bht}{bht_ghr_pad}= "fghr[6:4],3'b0";
#    $config{bht}{bht_ghr_pad2}= "fghr[6:3],2'b0";
#    $config{bht}{bht_array_depth}= 128;
#    $config{bht}{bht_addr_hi}= 10;
#} elsif($config{bht}{bht_size}==512){
#    $config{bht}{bht_ghr_size}= 6;
#    $config{bht}{bht_ghr_range}= "5:0";
#    $config{bht}{bht_ghr_pad}= "fghr[5:4],3'b0";
#    $config{bht}{bht_ghr_pad2}= "fghr[5:3],2'b0";
#    $config{bht}{bht_array_depth}= 64;
#    $config{bht}{bht_addr_hi}= 9;
#} elsif($config{bht}{bht_size}==256){
#    $config{bht}{bht_ghr_size}= 5;
#    $config{bht}{bht_ghr_range}= "4:0";
#    $config{bht}{bht_ghr_pad}= "fghr[4],3'b0";
#    $config{bht}{bht_ghr_pad2}= "fghr[4:3],2'b0";
#    $config{bht}{bht_addr_hi} = 8;
#    $config{bht}{bht_array_depth}= 32;
#} elsif($config{bht}{bht_size}==128){
#    $config{bht}{bht_ghr_size}= 5;
#    $config{bht}{bht_ghr_range}= "4:0";
#    $config{bht}{bht_ghr_pad}= "fghr[4],3'b0";
#    $config{bht}{bht_ghr_pad2}= "fghr[4:3],2'b0";
#    $config{bht}{bht_addr_hi} = 7;
#    $config{bht}{bht_array_depth}= 16;
#} elsif($config{bht}{bht_size}==64){
#    $config{bht}{bht_ghr_size}= 4;
#    $config{bht}{bht_ghr_range}= "3:0";
#    $config{bht}{bht_ghr_pad}= "3'b0 ";
#    $config{bht}{bht_ghr_pad2}= "fghr[4],2'b0";
#    $config{bht}{bht_addr_hi} = 6;
#    $config{bht}{bht_array_depth}= 8;
#} elsif($config{bht}{bht_size}==32){
#    $config{bht}{bht_ghr_size}= 3;
#    $config{bht}{bht_ghr_range}= "2:0";
#    $config{bht}{bht_ghr_pad}= "2'b0 ";
#    $config{bht}{bht_ghr_pad2}= "2'b0";
#    $config{bht}{bht_addr_hi} = 5;
#    $config{bht}{bht_array_depth}= 4;
#    $config{bht}{bht_ghr_size_2} = 1;
#}
$config{bht}{bht_ghr_hash_1} = ($config{bht}{bht_ghr_size} > ($config{btb}{btb_index1_hi}-2));

$config{bht}{bht_hash_string} = &ghrhash($config{btb}{btb_index1_hi}, $config{bht}{bht_ghr_size});

$config{pic}{pic_base_addr} = (hex($config{pic}{pic_region})<<28) +
    (hex($config{pic}{pic_offset}));
$config{pic}{pic_base_addr} = sprintf("0x%x", $config{pic}{pic_base_addr});

$config{pic}{pic_int_words} = int($config{pic}{pic_total_int}/32 +0.9);
$config{pic}{pic_bits} = 10 + log2($config{pic}{pic_size});

$config{core}{lsu_num_nbload_width} = log2($config{core}{lsu_num_nbload});

$config{dccm}{dccm_sadr} = (hex($config{dccm}{dccm_region})<<28) +
    (hex($config{dccm}{dccm_offset}));
$config{dccm}{dccm_sadr} = sprintf("0x%x", $config{dccm}{dccm_sadr});

$config{dccm}{dccm_eadr} = (hex($config{dccm}{dccm_region})<<28) +
    (hex($config{dccm}{dccm_offset})) + size($config{dccm}{dccm_size})-1;
$config{dccm}{dccm_eadr} = sprintf("0x%x", $config{dccm}{dccm_eadr});

$config{dccm}{dccm_reserved} = sprintf("0x%x", ($config{dccm}{dccm_size}>=16)? 5120 : ($config{dccm}{dccm_size}*1024)/4);

$config{dccm}{dccm_bits} =  ($config{dccm}{dccm_size}==48 ) ? 16 : 10 + log2($config{dccm}{dccm_size});

$config{dccm}{dccm_bank_bits} = log2($config{dccm}{dccm_num_banks});
$config{dccm}{dccm_data_width} = 32;
$config{dccm}{dccm_fdata_width} = $config{dccm}{dccm_data_width} + log2($config{dccm}{dccm_data_width}) + 2;
$config{dccm}{dccm_byte_width} = $config{dccm}{dccm_data_width}/8;

$config{dccm}{dccm_width_bits} = log2($config{dccm}{dccm_byte_width});
$config{dccm}{dccm_index_bits} = $config{dccm}{dccm_bits} - $config{dccm}{dccm_bank_bits} - $config{dccm}{dccm_width_bits};

$config{dccm}{dccm_ecc_width} = log2($config{dccm}{dccm_data_width}) + 2;
$config{dccm}{lsu_sb_bits}    = (($config{dccm}{dccm_bits}) > ($config{pic}{pic_bits})) ? ($config{dccm}{dccm_bits}) : ($config{pic}{pic_bits});
$config{dccm}{dccm_rows}      = ($config{dccm}{dccm_size}==48 ) ? (2**($config{dccm}{dccm_index_bits}-1) +  2**$config{dccm}{dccm_index_bits})/2   : 2**$config{dccm}{dccm_index_bits};
$config{dccm}{dccm_data_cell} = "ram_$config{dccm}{dccm_rows}x39";

$config{icache}{icache_num_lines}      = $config{icache}{icache_size}*1024/$config{icache}{icache_ln_sz};
$config{icache}{icache_num_lines_way}  = $config{icache}{icache_num_lines}/$config{icache}{icache_num_ways};
$config{icache}{icache_num_lines_bank} = $config{icache}{icache_num_lines}/($config{icache}{icache_num_ways} * $config{icache}{icache_banks_way});
$config{icache}{icache_data_depth}     = $config{icache}{icache_num_lines_bank} * $config{icache}{icache_ln_sz} /$config{icache}{icache_bank_width};
$config{icache}{icache_data_index_lo}  = log2($config{icache}{icache_bank_width}) + log2($config{icache}{icache_banks_way});
$config{icache}{icache_index_hi}       = $config{icache}{icache_data_index_lo} + log2($config{icache}{icache_data_depth}) -1;
$config{icache}{icache_bank_hi}        = $config{icache}{icache_data_index_lo}  - 1;
$config{icache}{icache_bank_lo}        = log2($config{icache}{icache_bank_width});
$config{icache}{icache_tag_index_lo}   = log2($config{icache}{icache_ln_sz});
$config{icache}{icache_tag_lo}         = log2($config{icache}{icache_num_lines_way}) + $config{icache}{icache_tag_index_lo};
$config{icache}{icache_tag_depth}      = $config{icache}{icache_num_lines}/$config{icache}{icache_num_ways};
$config{icache}{icache_data_width}     = 8*$config{icache}{icache_bank_width};

$config{icache}{icache_bank_bits}     = 1+$config{icache}{icache_bank_hi}-$config{icache}{icache_bank_lo};
$config{icache}{icache_status_bits}   =  $config{icache}{icache_num_ways}-1;
$config{icache}{icache_num_beats}     = ($config{icache}{icache_ln_sz}==64) ? 8 : 4;
$config{icache}{icache_beat_bits}     = ($config{icache}{icache_ln_sz}==64) ? 3 : 2;
$config{icache}{icache_scnd_last}     = ($config{icache}{icache_ln_sz}==64) ? 6 : 2;
$config{icache}{icache_beat_addr_hi}  = ($config{icache}{icache_ln_sz}==64) ? 5 : 4;

if (($config{icache}{icache_ecc})) {
$config{icache}{icache_fdata_width} = $config{icache}{icache_data_width} + 7;
$config{icache}{icache_data_cell}   = "ram_$config{icache}{icache_data_depth}x$config{icache}{icache_fdata_width}";
$config{icache}{icache_tag_cell}    = ($config{icache}{icache_tag_depth} == 32) ? "ram_$config{icache}{icache_tag_depth}x26" : "ram_$config{icache}{icache_tag_depth}x25";

}
else {
$config{icache}{icache_fdata_width} = $config{icache}{icache_data_width} + 4;
$config{icache}{icache_data_cell}   = "ram_$config{icache}{icache_data_depth}x$config{icache}{icache_fdata_width}";
$config{icache}{icache_tag_cell}    = "ram_$config{icache}{icache_tag_depth}x21";
}

$config{pic}{pic_total_int_plus1} = $config{pic}{pic_total_int} + 1;
# Defines with explicit values in the macro name
$config{dccm}{"dccm_num_banks_$config{dccm}{dccm_num_banks}"} = "";
$config{dccm}{"dccm_size_$config{dccm}{dccm_size}"} = "";

# If ICCM offset not explicitly provided, align to TOP of the region
if ($top_align_iccm && ($config{iccm}{iccm_offset} eq $iccm_offset) && ($config{iccm}{iccm_size} < 32)) {
    $config{iccm}{iccm_region} = "0xa";
    print "$self: Setting default iccm region to region $config{iccm}{iccm_region}\n";
    $config{iccm}{iccm_offset} = sprintf("0x%08x",256*1024*1024-size($config{iccm}{iccm_size}));
    print "$self: Aligning default iccm offset to top of region @ $config{iccm}{iccm_offset}\n";
}
$config{iccm}{iccm_sadr} = (hex($config{iccm}{iccm_region})<<28) +
                           (hex($config{iccm}{iccm_offset}));
$config{iccm}{iccm_sadr} = sprintf("0x%08x", $config{iccm}{iccm_sadr});

$config{iccm}{iccm_eadr} = (hex($config{iccm}{iccm_region})<<28) +
                           (hex($config{iccm}{iccm_offset})) + size($config{iccm}{iccm_size})-1;
$config{iccm}{iccm_eadr} = sprintf("0x%08x", $config{iccm}{iccm_eadr});

$config{iccm}{iccm_reserved} = sprintf("0x%x", ($config{iccm}{iccm_size}>30)? 4096 : ($config{iccm}{iccm_size}*1024)/4);

$config{iccm}{iccm_bits}       = 10 + log2($config{iccm}{iccm_size});
$config{iccm}{iccm_bank_bits}  = log2($config{iccm}{iccm_num_banks});  //-1;
$config{iccm}{iccm_index_bits} = $config{iccm}{iccm_bits} - $config{iccm}{iccm_bank_bits} - 2;   # always 4 bytes
$config{iccm}{iccm_rows}       = 2**$config{iccm}{iccm_index_bits};
$config{iccm}{iccm_data_cell}  = "ram_$config{iccm}{iccm_rows}x39";

$config{iccm}{iccm_bank_hi}        = 2+$config{iccm}{iccm_bank_bits}-1;
$config{iccm}{iccm_bank_index_lo}  = 1+$config{iccm}{iccm_bank_hi};



# Defines with explicit values in the macro name
$config{iccm}{"iccm_num_banks_$config{iccm}{iccm_num_banks}"} = "";
$config{iccm}{"iccm_size_$config{iccm}{iccm_size}"} = "";

# Track used regions

$regions_used{hex($config{iccm}{iccm_region})} = 1;
$regions_used{hex($config{dccm}{dccm_region})} = 1;
$regions_used{hex($config{pic}{pic_region})} = 1;
$regions_used{hex($config{reset_vec})>>28} = 1;

# Find an unused region for serial IO
for (my $rgn = 15;$rgn >= 0; $rgn--) {
    if (($rgn != hex($config{iccm}{iccm_region})) &&
        ($rgn != hex($config{dccm}{dccm_region})) &&
        ($rgn != (hex($config{pic}{pic_region})))) {
        $config{memmap}{serialio} = ($rgn << 28) + (22<<18);
        $regions_used{$rgn} = 1;
        last;
    }
}

$config{memmap}{serialio} = sprintf("0x%08x", $config{memmap}{serialio});

# Find an unused region for external data
for (my $rgn = 15;$rgn >= 0; $rgn--) {
    if (($rgn != hex($config{iccm}{iccm_region})) &&
        ($rgn != hex($config{dccm}{dccm_region})) &&
        ($rgn != (hex($config{memmap}{serialio})>>28)) &&
        ($rgn != (hex($config{pic}{pic_region})))) {
        $config{memmap}{external_data} = ($rgn << 28) + (22<<18);
        $regions_used{$rgn} = 1;
        last;
    }
}
$config{memmap}{external_data} = sprintf("0x%08x", $config{memmap}{external_data});
#
# Find an unused region for external prog
for (my $rgn = 15;$rgn >= 0; $rgn--) {
    if (($rgn != hex($config{iccm}{iccm_region})) &&
        ($rgn != hex($config{dccm}{dccm_region})) &&
        ($rgn != (hex($config{memmap}{serialio})>>28)) &&
        ($rgn != (hex($config{memmap}{external_data})>>28)) &&
        ($rgn != (hex($config{pic}{pic_region})))) {
        $config{memmap}{external_prog} = ($rgn << 28);
        $regions_used{$rgn} = 1;
        last;
    }
}
$config{memmap}{external_prog} = sprintf("0x%08x", $config{memmap}{external_prog});

# Unused region for second data
for (my $rgn = 15;$rgn >= 0; $rgn--) {
    if (($rgn != hex($config{iccm}{iccm_region})) &&
        ($rgn != hex($config{dccm}{dccm_region})) &&
        ($rgn != (hex($config{memmap}{serialio})>>28)) &&
        ($rgn != (hex($config{memmap}{external_data})>>28)) &&
        ($rgn != (hex($config{memmap}{external_prog})>>28)  &&
        ($rgn != (hex($config{pic}{pic_region})))
        )) {
        $config{memmap}{external_data_1} = ($rgn << 28);
        $regions_used{$rgn} = 1;
        last;
    }
}
$config{memmap}{external_data_1} = sprintf("0x%08x", $config{memmap}{data_1});


#$config{memmap}{consoleio} = hex($config{memmap}{serialio}) + 0x100;
#$config{memmap}{consoleio} = sprintf("0x%x", $config{memmap}{consoleio});

# Find an unused region for debug_sb_memory data
for (my $rgn = 15;$rgn >= 0; $rgn--) {
    if (($rgn != hex($config{iccm}{iccm_region})) &&
        ($rgn != hex($config{dccm}{dccm_region})) &&
        ($rgn != (hex($config{memmap}{serialio})>>28)) &&
        ($rgn != (hex($config{memmap}{external_data})>>28)) &&
        ($rgn != (hex($config{memmap}{external_data_1})>>28)) &&
        ($rgn != (hex($config{pic}{pic_region})))) {
        $config{memmap}{debug_sb_mem} = ($rgn << 28) + (22<<18);
        $regions_used{$rgn} = 1;
        last;
    }
}
$config{memmap}{debug_sb_mem} = sprintf("0x%08x", $config{memmap}{debug_sb_mem});


# Create the memory map hole for random testing
# Only do this if masks are not enabled already
if (hex($config{protection}{data_access_enable0}) > 0 ||
    hex($config{protection}{data_access_enable1}) > 0 ||
    hex($config{protection}{data_access_enable2}) > 0 ||
    hex($config{protection}{data_access_enable3}) > 0 ||
    hex($config{protection}{data_access_enable4}) > 0 ||
    hex($config{protection}{data_access_enable5}) > 0 ||
    hex($config{protection}{data_access_enable6}) > 0 ||
    hex($config{protection}{data_access_enable7}) > 0 ||
    hex($config{protection}{inst_access_enable0}) > 0 ||
    hex($config{protection}{inst_access_enable1}) > 0 ||
    hex($config{protection}{inst_access_enable2}) > 0 ||
    hex($config{protection}{inst_access_enable3}) > 0 ||
    hex($config{protection}{inst_access_enable4}) > 0 ||
    hex($config{protection}{inst_access_enable5}) > 0 ||
    hex($config{protection}{inst_access_enable6}) > 0 ||
    hex($config{protection}{inst_access_enable7}) > 0) {
    delete($config{memmap}{external_mem_hole}) ;
} else {
    # Unused region to create a memory map hole
    for (my $rgn = 15;$rgn >= 0; $rgn--) {
        if (!defined($regions_used{$rgn})) {
            $config{memmap}{external_mem_hole} = ($rgn << 28);
            $regions_used{$rgn} = 1;
            last;
        }
    }
    if ($config{memmap}{external_mem_hole} == 0) {
        $config{protection}{data_access_addr0} = "0x10000000";
        $config{protection}{data_access_mask0} = "0xffffffff";
        $config{protection}{data_access_enable0} = "1";
    } elsif (($config{memmap}{external_mem_hole}>>28) == 16) {
        $config{protection}{data_access_addr0} = "0x00000000";
        $config{protection}{data_access_mask0} = "0xefffffff";
        $config{protection}{data_access_enable0} = "1";
    } else {
        my $hreg = $config{memmap}{external_mem_hole}>>28;
        $config{protection}{data_access_addr0} = sprintf("0x%x", (($hreg^8)&8)<<28);
        $config{protection}{data_access_mask0} = "0x7fffffff";
        $config{protection}{data_access_addr1} = sprintf("0x%x", ($hreg&8) << 28 |(($hreg^4)&4)<<28);
        $config{protection}{data_access_mask1} = "0x3fffffff";
        $config{protection}{data_access_addr2} = sprintf("0x%x", ($hreg&12) <<28 | (($hreg^2)&2) <<28);
        $config{protection}{data_access_mask2} = "0x1fffffff";
        $config{protection}{data_access_addr3} = sprintf("0x%x", ($hreg&14) << 28 |(($hreg^1)&1)<<28);
        $config{protection}{data_access_mask3} = "0x0fffffff";
        $config{protection}{data_access_enable0} = "1";
        $config{protection}{data_access_enable1} = "1";
        $config{protection}{data_access_enable2} = "1";
        $config{protection}{data_access_enable3} = "1";
        $config{protection}{inst_access_addr0} = sprintf("0x%x", (($hreg^8)&8)<<28);
        $config{protection}{inst_access_mask0} = "0x7fffffff";
        $config{protection}{inst_access_addr1} = sprintf("0x%x", ($hreg&8) << 28 |(($hreg^4)&4)<<28);
        $config{protection}{inst_access_mask1} = "0x3fffffff";
        $config{protection}{inst_access_addr2} = sprintf("0x%x", ($hreg&12) <<28 | (($hreg^2)&2) <<28);
        $config{protection}{inst_access_mask2} = "0x1fffffff";
        $config{protection}{inst_access_addr3} = sprintf("0x%x", ($hreg&14) << 28 |(($hreg^1)&1)<<28);
        $config{protection}{inst_access_mask3} = "0x0fffffff";
        $config{protection}{inst_access_enable0} = "1";
        $config{protection}{inst_access_enable1} = "1";
        $config{protection}{inst_access_enable2} = "1";
        $config{protection}{inst_access_enable3} = "1";
    }
    $config{memmap}{external_mem_hole} = sprintf("0x%08x", $config{memmap}{external_mem_hole});
}

#Define 5 unused regions for used in TG

foreach my $unr (reverse(0 .. 15)) {
    if (!defined($regions_used{$unr})) {
        $config{memmap}{"unused_region$unr"} = sprintf("0x%08x",($unr << 28));
        $regions_used{$unr} = 1;
    }
}

if ($target eq "baseline") {
    $config{reset_vec} = $config{iccm}{iccm_sadr};
    $config{testbench}{magellan} = 1;
    print "$self: Setting reset_vec = ICCM start address for Baseline\n";
}

if ($target eq "default_iccm_mt") {
    $config{reset_vec} = $config{iccm}{iccm_sadr};
    $config{iccm_fetch_only} = 1;
    print "$self: Setting reset_vec = ICCM start address for Baseline\n";
}


# Output bit-width specifiers for these variables
our %widths = (
        "dccm_region"         => "4",
        "dccm_offset"         => "28",
        "dccm_sadr"           => "32",
        "dccm_eadr"           => "32",
        "pic_region"          => "4",
        "pic_offset"          => "10",
        "pic_base_addr"       => "32",
        "iccm_region"         => "4",
        "iccm_offset"         => "10",
        "iccm_sadr"           => "32",
        "iccm_eadr"           => "32",
        "bus_prty_default"    => "2",
        "inst_access_enable0" => "1",
        "inst_access_enable1" => "1",
        "inst_access_enable2" => "1",
        "inst_access_enable3" => "1",
        "inst_access_enable4" => "1",
        "inst_access_enable5" => "1",
        "inst_access_enable6" => "1",
        "inst_access_enable7" => "1",
        "data_access_enable0" => "1",
        "data_access_enable1" => "1",
        "data_access_enable2" => "1",
        "data_access_enable3" => "1",
        "data_access_enable4" => "1",
        "data_access_enable5" => "1",
        "data_access_enable6" => "1",
        "data_access_enable7" => "1",
);
#}}}


#print Dumper(\%config);
#print Dumper(\%width);

#print Dumper(\%sets);
#print Dumper(\%unsets);

# Sanity checks
check_addr_align("dccm", hex($config{dccm}{dccm_sadr}), $config{dccm}{dccm_size}*1024);
check_addr_align("iccm", hex($config{iccm}{iccm_sadr}), $config{iccm}{iccm_size}*1024);
check_addr_align("pic", hex($config{pic}{pic_base_addr}), $config{pic}{pic_size}*1024);

#   Prevent overlap of internal memories
if ((hex($config{pic}{pic_region}) == hex($config{iccm}{iccm_region})) && (hex($config{pic}{pic_offset}) == hex($config{iccm}{iccm_offset}))) {
    die "$self: ERROR! PIC and ICCM blocks collide (region $config{iccm}{iccm_region}, offset $config{pic}{pic_offset})!\n";
}
if ((hex($config{pic}{pic_region}) == hex($config{dccm}{dccm_region})) && (hex($config{pic}{pic_offset}) == hex($config{dccm}{dccm_offset}))) {
    die "$self: ERROR! PIC and DCCM blocks collide (region $config{dccm}{dccm_region}, offset $config{pic}{pic_offset})!\n";
}
if ((hex($config{iccm}{iccm_region}) == hex($config{dccm}{dccm_region})) && (hex($config{iccm}{iccm_offset}) == hex($config{dccm}{dccm_offset}))) {
    die "$self: ERROR! ICCM and DCCM blocks collide (region $config{iccm}{iccm_region}, offset $config{dccm}{dccm_offset})!\n";
}
#printf( "svc %s\n",$config{"testbench"}{"build_svc"});
#printf( "axi4 %s\n",$config{"testbench"}{"build_axi4"});
#printf( "ahb_lite %s\n",$config{"testbench"}{"build_ahb_lite"});
#printf( "axi_native %s\n",$config{"testbench"}{"build_axi_native"});

# all targets default to axi
if (($config{testbench}{build_ahb_lite} == 1)) {
    delete $config{testbench}{build_svc};
    delete $config{testbench}{build_axi4};
    $config{testbench}{build_axi_native}=1;
    $verilog_parms{build_svc} = 0;
    $verilog_parms{build_axi4} = 0;
} else {
    $config{testbench}{build_axi_native}=1;
    $config{testbench}{build_axi4} = 1;
    delete $config{testbench}{build_ahb_lite};
    delete $config{testbench}{build_svc};
    $verilog_parms{build_ahb_lite} = 0;
    $verilog_parms{build_svc} = 0;
}


# If 1 thread: No PIC delegation. CSRs mhartnum, nmipdel and
# mhartstart get adjusted.
if ($config{core}{num_threads} == 1) {
    delete $config{pic}{pic_meidels_offset};
    $config{csr}{mhartnum}{reset}  = "0x1" if exists $config{csr}{mhartnum};
    $config{csr}{mnmipdel}{mask}   = "0x1" if exists $config{csr}{mnmipdel};
    $config{csr}{mhartstart}{mask} = "0x0" if exists $config{csr}{mhartstart};
}


#printf( "svc %s\n",$config{"testbench"}{"build_svc"});
#printf( "axi4 %s\n",$config{"testbench"}{"build_axi4"});
#printf( "ahb_lite %s\n",$config{"testbench"}{"build_ahb_lite"});
#printf( "axi_native %s\n",$config{"testbench"}{"build_axi_native"});

# Disable Bus barrier and 64b for AXI
if (defined($config{"testbench"}{"build_axi_native"}) && ($config{"testbench"}{"build_axi_native"} ne "0")) {
    if (! (defined($config{testbench}{build_ahb_lite}) && $config{testbench}{build_ahb_lite} ne "0")) {
        $config{csr}{mfdc}{reset} = "0x00070040" if exists $config{csr}{mfdc};
    }
}

# AHB overrides
if (defined($config{"testbench"}{"build_ahb_lite"})  && ($config{"testbench"}{"build_ahb_lite"} ne "0")) {
}

# Set testbench only defines
if ($config{"core"}{"num_threads"} == 1) {
    delete $config{"testbench"}{"mt_build"};
} else {
    $config{"testbench"}{"mt_build"} = 1;
}


# parm processing before any values are deleted from the hash

print "$self: Writing $tdfile\n";
print "$self: Writing $paramfile\n";
open (FILE1, ">$tdfile") || die "Cannot open $tdfile for writing $!\n";
open (FILE2, ">$paramfile") || die "Cannot open $paramfile for writing $!\n";
print_header("//");
gen_define("","`", \%config, \%verilog_parms, \@verilog_vars);
dump_parms(\%verilog_parms);
close FILE1;
close FILE2;

$config{config_key}="32'hdeadbeef";

# end parms

# deletes

if (($load_to_use_plus1==0) && !grep(/load_to_use_plus1/, @sets)) { delete $config{"core"}{"load_to_use_plus1"}; }
if (($load_to_use_bus_plus1==0) && !grep(/load_to_use_bus_plus1/, @sets)) { delete $config{"core"}{"load_to_use_bus_plus1"}; }
if (($iccm_enable==0) && !grep(/iccm_enable/, @sets)) { delete $config{"iccm"}{"iccm_enable"}; }
if (($dccm_enable==0) && !grep(/dccm_enable/, @sets)) { delete $config{"dccm"}{"dccm_enable"}; }
if (($icache_enable==0) && !grep(/icache_enable/, @sets)) { delete $config{"icache"}{"icache_enable"}; }
if (($icache_waypack==0) && !grep(/icache_waypack/, @sets)) { delete $config{"icache"}{"icache_waypack"}; }
if (($opensource==0) && !grep(/opensource/, @sets))   { delete $config{"core"}{"opensource"}; }
if (($verilator==0) && !grep(/verilator/, @sets))     { delete $config{"core"}{"verilator"}; }
if (($no_secondary_alu==0) && !grep(/no_secondary_alu/, @sets))           { delete $config{"core"}{"no_secondary_alu"}; }
if (($pic_2cycle==0) && !grep(/pic_2cycle/, @sets)) { delete $config{"pic"}{"pic_2cycle"}; }
if (($icache_ecc==0) && !grep(/icache_ecc/, @sets)) { delete $config{"icache"}{"icache_ecc"}; }
if (($icache_2banks==0) && !grep(/icache_2banks/, @sets)) { delete $config{"icache"}{"icache_2banks"}; }



##################### Add dumper routines here ##########################
#
# Dump Verilog $RV_ROOT/configs/common_defines.vh
print "$self: Writing $vlogfile\n";
open (FILE, ">$vlogfile") || die "Cannot open $vlogfile for writing $!\n";
print_header("//");
print FILE "`define RV_ROOT \"".$ENV{RV_ROOT}."\"\n";
gen_define("","`", \%config, "", \@verilog_vars);
close FILE;

print "$self: Writing $asmfile\n";
open (FILE, ">$asmfile") || die "Cannot open $asmfile for writing $!\n";
# Dump ASM/C   $RV_ROOT/diags/env/defines.h
print_header("//");
gen_define("","#", \%config, "", \@asm_vars, \@asm_overridable);
close FILE;

# add `define PHYSICAL 1
# remove `undef RV_ICCM_ENABLE

my $pddata='
`include "common_defines.vh"
`undef ASSERT_ON
`undef TEC_RV_ICG
`define TEC_RV_ICG HDBLVT16_CKGTPLT_V5_12
`define PHYSICAL 1
';


print "$self: Writing $pdfile\n";
open (FILE, ">$pdfile") || die "Cannot open $pdfile for writing $!\n";
# Dump PD   $RV_ROOT/$RV_ROOT/configs/pd_defines.vh
print_header("//");
printf (FILE "$pddata");
close FILE;

print "$self: Writing $whisperfile\n";
dump_whisper_config(\%config, $whisperfile);


# change this to use config version
`$ENV{RV_ROOT}/tools/picmap -t $config{pic}{pic_total_int} -n $config{core}{num_threads} > $build_path/pic_map_auto.h`;
#`$ENV{RV_ROOT}/tools/unrollforverilator  $config{pic}{pic_total_int_plus1} > $build_path/eh2_pic_ctrl_verilator_unroll.sv`;

# Perl vars for use by scripts
print "$self: Writing $perlfile\n";
open (FILE, ">$perlfile") || die "Cannot open $perlfile for writing $!\n";
print_header("# ");
print FILE "# To use this in a perf script, use 'require \$RV_ROOT/configs/config.pl'\n";
print FILE "# Reference the hash via \$config{name}..\n\n\n";
print FILE Data::Dumper->Dump([\%config], [ qw(*config) ]);
print FILE "1;\n";
close FILE;

# Done ##################################################################
#
exit(0);

# ######################   Helper subroutines ##########################{{{
# Convert size in kilobytes to real value

sub size {#{{{
    my $ksize = shift;
    my $size = sprintf("%d",$ksize*1024);
    return $size;
}#}}}

# Print the defines with prefix
sub print_define {#{{{
    my ($sym, $key,$value, $override) = @_;
    my $lprefix = $prefix if ($key !~ /$no_prefix/);
    if ($sym eq "`") {
        if (defined($widths{$key})) {
            $value =~ s/^(0x)*/$widths{$key}'h/;    # change to match elx2 swerv.config
        } else {
            $value =~ s/^0x/'h/;
        }
    }
    if ($defines_case eq "U") {
        print FILE "${sym}ifndef \U$lprefix$key\E\n" if ($override);
        print FILE "${sym}define \U$lprefix$key\E $value\n";
        print FILE "${sym}endif\n" if ($override);
    } else {
        print FILE "${sym}ifndef $lprefix$key\n" if ($override);
        print FILE "${sym}define $lprefix$key $value\n";
        print FILE "${sym}endif\n" if ($override);
    }
}#}}}

# print header
sub print_header {#{{{
    my $cs = shift;
    print FILE "$cs NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE\n";
    print FILE "$cs This is an automatically generated file by $ENV{USER} on ",`date`;
    print FILE "$cs\n$cs cmd:    $self @argv_orig \n";
    print FILE "$cs\n";
}#}}}

# evaluate derivations
sub derive {#{{{
    my $eqn = shift;
    return sprintf("0x%x", eval($eqn));
}#}}}

# traverse the database and extract the key/value pair
sub gen_define {#{{{
    my $matched = shift;
    my $prefix = shift;
    my $hash = @_[0];
    my $parms = @_[1];
    my @printvars = @{@_[2]};
    my @overridable = @{@_[3]} if defined @_[3];  # change to match elx2 swerv.config
    my $re = join("|",@printvars);
    $re = qr/($re)/;
    #print Dumper($hash);
    foreach my $key (keys %$hash) {
        next if $key eq "csr";
        #print "looking at $key:$matched ($re)\n";
        if (defined($unsets{$key})) {
            print "$self:unsetting $key\n";
            delete($config{$key});
            if ($parms and defined($parms->{$key})) {
                $parms->{$key} = 0;
            }
            next
        }
        if (defined($sets{$key}) && $sets{$key} ne $$hash{$key}) {
            if (($$hash{$key} =~ /derived/i) && ($$hash{$key} !~ /overridable/i)) {
                die ("$self: ERROR! $key is a derived and non-overridable parameter!\n");
            } else {
                print "$self: Overriding $key value $$hash{$key} with $sets{$key}\n";
                $$hash{$key} = $sets{$key};
            }
        }
        my $value = $$hash{$key};
        if (ref($value) eq "HASH") {
            if ($key =~ /$re/) {
                $matched = 1;
            }
            gen_define($matched,$prefix, $value, $parms, \@printvars, \@overridable);
            $matched = 0;
        } elsif (ref($value) eq "ARRAY") {
            # print "$key : @{$value}\n";
             $matched = 0;
        } else {
            if ($matched eq "1" || $key =~ /$re/) {
                if($value =~ /derive\(.*\)/o) {
                    $value = eval($value);
                }
                my $override = grep(/^$key$/, @overridable);
                print_define($prefix, $key, $value, $override);
                #printf("$key = $value\n");
                if ($parms and defined($parms->{$key})) {    # change to match elx2 swerv.config
                    $value=decimal($value);
                    #printf("verilog parm $key = $value %s\n",$parms->{$key});
                    $value=d2b($key,$value,$parms->{$key});
                    #printf("verilog parm $key = $value\n");
                    $parms->{$key}=$value;
                }
            }
        }
    }
}#}}}

sub dump_define {#{{{
    my $matched = shift;
    my $prefix = shift;
    my $hash = @_[0];
    my @printvars = @{@_[1]};
    my @overridable = @{@_[2]} if defined @_[2];   # change to match elx2 swerv.config
    my $re = join("|",@printvars);
    $re = qr/($re)/;
    #print Dumper($hash);
    foreach my $key (keys %$hash) {
        next if $key eq "csr";
        next unless $matched || grep(/^$key$/,@dvars);
        #print "looking at $key:$matched ($re)\n";
        if (defined($unsets{$key})) {
            print "$self:unsetting $key\n";
            delete($config{$key});
            next
        }
        if (defined($sets{$key}) && $sets{$key} ne $$hash{$key}) {
            if (($$hash{$key} =~ /derived/i) && ($$hash{$key} !~ /overridable/i)) {
                die ("$self: ERROR! $key is a derived and non-overridable parameter!\n");
            } else {
                print "$self: Overriding $key value $$hash{$key} with $sets{$key}\n";
                $$hash{$key} = $sets{$key};
            }
        }
        my $value = $$hash{$key};
        if (ref($value) eq "HASH") {
            if ($key =~ /$re/) {
                $matched = 1;
            }
            dump_define($matched,$prefix, $value, \@printvars, \@overridable);
            $matched = 0;
        } elsif (ref($value) eq "ARRAY") {
            # print "$key : @{$value}\n";
             $matched = 0;
        } else {
            if ($matched eq "1" || $key =~ /$re/) {
                if($value =~ /derive\(.*\)/o) {
                    $value = eval($value);
                }
                printf ("swerv: %-30s = $value\n",$key) if ($value !~ /derived/);
            }
        }
    }
}#}}}

# Perform cmd line set/unset ############################################{{{
sub map_set_unset {
    if (scalar(@sets)) {
        print "$self: Set(s) requested : @sets\n";
        foreach (@sets) {
            my ($key,$value) = m/(\w+)=*(\w+)*/o;
            $value = 1 if (!defined($value));
            $sets{$key} = $value;
        }
    }
    if (scalar(@unsets)) {
        print "$self: Unset(s) requested : @sets\n";
        foreach (@unsets) {
            $unsets{$_} = 1;
        }
    }
} #}}}
#}}}


# If arg looks like a hexadecimal string, then convert it to decimal.#{{{
# Otherwise, return arg.
sub decimal {
    my ($x) = @_;
    return hex($x)  if $x =~ /^0x/o;
    return $x;
}#}}}

# Collect memory protection specs (array of address pairs) in the given
# resutls array. Tag is either "data" or "inst".
sub collect_mem_protection {
    my ($tag, $config, $results) = @_;
    return unless exists $config{protection};

    my $prot = $config{protection};

    my $enable_tag = $tag . "_access_enable";
    my $addr_tag = $tag . "_access_addr";
    my $mask_tag = $tag . "_access_mask";

    foreach my $key (keys %{$prot}) {
        next unless $key =~ /^$enable_tag(\d+)$/;
        my $ix = $1;

        my $enable = $prot->{$key};
        if ($enable !~ /[01]$/) {
            warn("Invalid value for protection entry $key: $enable\n");
            next;
        }

        next unless ($enable eq "1" or $enable eq "1'b1");

        if (! exists $prot->{"$addr_tag$ix"}) {
            warn("Missing $addr_tag$ix\n");
            next;
        }

        if (! exists $prot->{"$mask_tag$ix"}) {
            warn("Missing $mask_tag$ix\n");
            next;
        }

        my $addr = $prot->{"$addr_tag$ix"};
        my $mask = $prot->{"$mask_tag$ix"};

        if ($addr !~ /^0x[0-9a-fA-F]+$/) {
            warn("Invalid $addr_tag$ix: $addr\n");
            next;
        }

        if ($mask !~ /^0x[0-9a-fA-F]+$/) {
            warn("Invalid $mask_tag$ix: $mask\n");
            next;
        }

        if ((hex($addr) & hex($mask)) != 0) {
            warn("Protection mask bits overlap address bits in mask $mask and addr $addr\n");
        }

        if ($mask !~ /^0x0*[137]?f*$/) {
            warn("Protection mask ($mask) must have all its one bits to the right of its zero bits\n");
            next;
        }

        my $start = hex($addr) & ~hex($mask) & 0xffffffff;
        my $end = (hex($addr) | hex($mask)) & 0xffffffff;

        $start = sprintf("0x%08x", $start);
        $end = sprintf("0x%08x", $end);

        push(@{$results}, [ $start, $end ]);
    }
}


# Collect the memory mapped registers associated with the pic (platform
# interrup controller) to include in the whisper.json file.
sub collect_mem_mapped_regs {
    my ($pic, $results) = @_;
    my $default_mask = 0;
    $results->{default_mask} = $default_mask;
    my $addr = hex($pic->{pic_region})*256*1024*1024 + hex($pic->{pic_offset});
    $results->{address} = sprintf("0x%x", $addr);
    $results->{size} = sprintf("0x%x", $pic->{pic_size}*1024);

    my @names = qw ( mpiccfg meipl meip meie meigwctrl meigwclr meidels );
    $results->{registers} = {};
    foreach my $name (@names) {
        my $tag = "pic_${name}_offset";
        next unless exists $pic->{$tag};
        my %item;
        my $offset = hex($pic->{$tag});
        $offset += 4  if ($name ne 'mpiccfg' and $name ne 'meip');
        $item{address} = sprintf("0x%x", $addr + $offset);
        $item{mask} = $pic->{"pic_${name}_mask"};
        $item{count} = $pic->{"pic_${name}_count"};
        $results->{registers}{$name} = \%item;
    }
}


sub dump_whisper_config{#{{{
    my ($config, $path) = @_;

    open(my $fh, ">", "$path") or die ("Failed to open $path for writing: $!\n");

    # Put the configuration parameters relevant to whisper into a hash
    # in preparation for a JSON dump.
    my %jh; # Json hash

    # Collect top-level integer entries.
    foreach my $tag (qw( harts xlen )) {
        $jh{$tag} = $config{$tag} + 0 if exists $config{$tag};
    }

    # Collect top-level string/hex entries.
    foreach my $tag (qw ( reset_vec nmi_vec num_mmode_perf_regs max_mmode_perf_event
                     even_odd_trigger_chains)) {
        $jh{$tag} = $config{$tag} if exists $config{$tag};
    }

    # Collect memory map configs.
    my (@inst_mem_prot, @data_mem_prot);
    collect_mem_protection("inst", $config, \@inst_mem_prot);
    collect_mem_protection("data", $config, \@data_mem_prot);
    $jh{memmap}{inst} = [@inst_mem_prot] if @inst_mem_prot;
    $jh{memmap}{data} = [@data_mem_prot] if @data_mem_prot;
    foreach my $tag (qw ( size page_size serialio )) {
        $jh{memmap}{tag} = $config{memmap}{ta} if exists $config{memmap}{tag};
    }

    # Collect load/store-error rollback parameter.
    if (exists $config{testbench} and exists $config{testbench}{sterr_rollback}) {
        $jh{store_error_rollback} = $config{testbench}{sterr_rollback};
    }
    if (exists $config{testbench} and exists $config{testbench}{lderr_rollback}) {
        $jh{load_error_rollback} = $config{testbench}{lderr_rollback};
    }

    # Collect dccm configs
    if (exists $config{dccm} and exists $config{dccm}{dccm_enable}) {
        $jh{dccm}{region} = $config{dccm}{dccm_region};
        $jh{dccm}{size} = 1024*decimal($config{dccm}{dccm_size}); # From 1k to bytes
        $jh{dccm}{offset} = $config{dccm}{dccm_offset};

        $jh{dccm}{size} = sprintf("0x%x", $jh{dccm}{size});
    }

    # Collect icccm configs.
    if (exists $config{iccm} and exists $config{iccm}{iccm_enable}) {
        $jh{iccm}{region} = $config{iccm}{iccm_region};
        $jh{iccm}{size} = 1024*decimal($config{iccm}{iccm_size}); # From 1k to bytes
        $jh{iccm}{offset} = $config{iccm}{iccm_offset};

        $jh{iccm}{size} = sprintf("0x%x", $jh{iccm}{size});
    }

    # Collect CSRs
    $jh{csr} = $config{csr} if exists $config{csr};

    # Collect CSRs not included in verilog.
    my @removed_csrs;

   if (!$config{core}{timer_legal_en}) {
        push(@removed_csrs, $_) for qw (mitcnt0 mitbnd0 mitctl0
                                        mitcnt1 mitbnd1 mitctl1);
    }

    # Collect fast interrupt enable.
    if (exists $config{core}{fast_interrupt_redirect}) {
        $jh{fast_interrupt_redirect} = $config{core}{fast_interrupt_redirect};
        # meicpct CSR is not built if fast interrupt.
        push(@removed_csrs, 'meicpct') if $jh{fast_interrupt_redirect};
    }

    # Remove CSRs not configured into verilog.
    delete $jh{csr}{$_} foreach @removed_csrs;

    # Collect pic configs.
    if (exists $config{pic}) {
        my %mem_mapped;
        collect_mem_mapped_regs($config{pic}, \%mem_mapped);
        $jh{'memory_mapped_registers'} = \%mem_mapped;

        # This is now deprecated. To be removed soon.
        while (my ($k, $v) = each %{$config{pic}}) {
            next if $k eq 'pic_base_addr';  # derived from region and offset
            if ($k eq 'pic_size') {
                $v *= 1024;  # from kbytes to bytes
                $v = sprintf("0x%x", $v);
            }
            $k =~ s/^pic_//o;
            $v += 0 if $v =~ /^\d+$/o;
            $jh{pic}{$k} = $v;
        }
    }

    # Make atomic instructions illegal outside of DCCM.
    $jh{amo_illegal_outside_dccm} = "true";

    # Make ld/st instructions trigger misaligned exceptions if base
    # address (value in rs1) and effective address refer to regions of
    # different types.
    $jh{effective_address_compatible_with_base} = "true";

    # Collect triggers.
    $jh{triggers} = $config{triggers} if exists $config{triggers};

    # Dump JSON config file.
    my $json = JSON->new->allow_nonref;
    my $text = $json->pretty->encode(\%jh);
    print($fh $text);

    close $fh;
}#}}}


# Checker for iccm/dccm/pic sub-region address alignment. Address must be a multiple
# of size  or next higher power of 2 if size is not a power of 2.
sub check_addr_align {
    my ($section, $addr, $size) = @_;

    die("Invalid $section size: $size\n")  if $size <= 0;

    my $log_size = log2($size);
    my $p2 = 1 << $log_size;
    $size = 2*$p2  if $size != $p2;

    if (($addr % $size) != 0) {
        printf("Address of $section area(0x%x) is not a multiple of its size (0x%x)\n",
               $addr, $size);
        exit(1);
    }
}


sub log2 {
    my ($n) = @_;
    return log($n)/log(2);
}

sub b2d {
    my ($v) = @_;

    $v = oct("0b" . $v);

    return($v);
}

sub d2b {
    my ($key,$v,$LEN) = @_;

    my $repeat;

    $v = sprintf "%b",$v;
    if (length($v)<$LEN) {
        $repeat=$LEN-length($v);
        $v="0"x$repeat.$v;
    }
    elsif (length($v)>$LEN) {
        die("d2b: parm $key value $v > len $LEN");
    }

    return($v);
}

sub invalid_mask {
    my ($m) = @_;

    if ($m =~ /^0x(0)*([137]?f+)$/) { return(0); }

    return(1);
}

sub b2h {
    my ($bin) = @_;

    # Make input bit string a multiple of 4
    $bin = substr("0000",length($bin)%4) . $bin if length($bin)%4;

    my ($hex, $nybble) = ("");
    while (length($bin)) {
        ($nybble,$bin) = (substr($bin,0,4), substr($bin,4));
        $nybble = eval "0b$nybble";
        $hex .= substr("0123456789ABCDEF", $nybble, 1);
    }
    return $hex;
}



sub dump_parms {
    my ($hash) = @_;

    my $bvalue;
    my $blen;
    my $upper;

    printf(FILE1 "typedef struct packed {\n");
    foreach my $key (sort keys %$hash) {
        $bvalue=$hash->{$key};
        $blen=length($bvalue);
        $upper=$key;
        $upper=~ tr/a-z/A-Z/;
        if ($blen==1) {
            printf(FILE1 "\tbit %-10s $upper;\n");
        }
        else {
            printf(FILE1 "\tbit %-10s $upper;\n",sprintf("[%d:0]",$blen-1));
        }
    }
   printf(FILE1 "} eh2_param_t;\n\n");

    my $bcat="";
    my $parmcnt=0;
    foreach my $key (sort keys %$hash) {
        #printf("// $key = %s\n",$verilog_parms{$key});
        $bcat.=$hash->{$key};
        $parmcnt++;
    }

    my $bvalue="";
    my $pcnt=0;
    my $delim=",";
    printf(FILE2 "parameter eh2_param_t pt = '{\n");
    foreach my $key (sort keys %$hash) {
        $upper=$key;
        $upper=~ tr/a-z/A-Z/;
        $pcnt++;
        if ($pcnt==$parmcnt) { undef $delim; }
        printf(FILE2 "\t%-22s : %d\'h%-10s $delim\n",$upper,length($hash->{$key}),b2h($hash->{$key}));
    }
    printf(FILE2 "}\n");

    printf(FILE2 "// parameter eh2_param_t pt = %d'h%s\n",length($bcat),b2h($bcat));

}

